Skip to content

Commit

Permalink
fix(compiler-cli): accept inheriting the constructor from a class in …
Browse files Browse the repository at this point in the history
…a library (#48156)

The stricter checks under `strictInjectionParameters` in Angular 15 now enforce that
an inherited constructor must be compatible with DI, based on whether all parameters
are valid injection tokens. There is an issue when the constructor is inherited from
a class in a declaration file though, as information on the usage of `@Inject()` is
not present within a declaration file. This means that this stricter check cannot be
accurately performed, resulting in false positives.

This commit disables the stricter check to behave the same as it did prior to
Angular 15, therefore avoiding the false positive.

Fixes #48152

PR Close #48156
  • Loading branch information
JoostK authored and dylhunn committed Nov 23, 2022
1 parent c3ccaa8 commit 86a21f5
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 1 deletion.
Expand Up @@ -14,7 +14,7 @@ import {DirectiveMeta, flattenInheritedDirectiveMetadata, HostDirectiveMeta, Met
import {describeResolvedType, DynamicValue, PartialEvaluator, ResolvedValue, traceDynamicValue} from '../../../partial_evaluator';
import {ClassDeclaration, ReflectionHost} from '../../../reflection';
import {DeclarationData, LocalModuleScopeRegistry} from '../../../scope';
import {identifierOfNode} from '../../../util/src/typescript';
import {identifierOfNode, isFromDtsFile} from '../../../util/src/typescript';

import {InjectableClassRegistry} from './injectable_registry';
import {isAbstractClassDeclaration, readBaseClass} from './util';
Expand Down Expand Up @@ -256,6 +256,14 @@ export function checkInheritanceOfInjectable(
return getInheritedUndecoratedCtorDiagnostic(node, classWithCtor.ref, kind);
}

if (isFromDtsFile(classWithCtor.ref.node)) {
// The inherited class is declared in a declaration file, in which case there is not enough
// information to detect invalid constructors as `@Inject()` metadata is not present in the
// declaration file. Consequently, we have to accept such occurrences, although they might
// still fail at runtime.
return null;
}

if (!strictInjectionParameters || isAbstractClassDeclaration(node)) {
// An invalid constructor is only reported as error under `strictInjectionParameters` and
// only for concrete classes; follow the same exclusions for derived types.
Expand Down
31 changes: 31 additions & 0 deletions packages/compiler-cli/test/ngtsc/ngtsc_spec.ts
Expand Up @@ -2960,6 +2960,37 @@ function allTests(os: string) {
`or add an explicit constructor to ConcreteDirWithoutCtor.`);
expect(getDiagnosticSourceCode(diags[2])).toBe('ConcreteDirWithoutCtor');
});

// https://github.com/angular/angular/issues/48152
it('should not give a compile-time error when a class inherits from foreign compilation unit',
() => {
env.tsconfig({strictInjectionParameters: true});
env.write('node_modules/external/index.d.ts', `
import * as i0 from '@angular/core';
export abstract class ExternalClass {
static ɵprov: i0.ɵɵInjectableDeclaration<ExternalClass>;
static ɵfac: i0.ɵɵFactoryDeclaration<ExternalClass, never>
constructor(invalid: string) {}
}
`);
env.write('test.ts', `
import {Directive} from '@angular/core';
import {ExternalClass} from 'external';
@Directive()
export abstract class AbstractMiddleDir extends ExternalClass {}
@Directive()
export class ConcreteMiddleDir extends AbstractMiddleDir {}
@Directive()
export class ConcreteDirWithoutCtor extends ConcreteMiddleDir {}
`);

env.driveMain();
});
});

describe('with strictInjectionParameters = false', () => {
Expand Down

0 comments on commit 86a21f5

Please sign in to comment.