From 86a21f5569bc4b8060a882bd3d542a6c002438c7 Mon Sep 17 00:00:00 2001 From: JoostK Date: Mon, 21 Nov 2022 21:55:42 +0100 Subject: [PATCH] fix(compiler-cli): accept inheriting the constructor from a class in 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 --- .../annotations/common/src/diagnostics.ts | 10 +++++- .../compiler-cli/test/ngtsc/ngtsc_spec.ts | 31 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/common/src/diagnostics.ts b/packages/compiler-cli/src/ngtsc/annotations/common/src/diagnostics.ts index b41b7fbbac6bd..5afd006f57bbf 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/common/src/diagnostics.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/common/src/diagnostics.ts @@ -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'; @@ -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. diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index c43f13c4d58c7..68d9c1ead3885 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -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; + static ɵfac: i0.ɵɵFactoryDeclaration + + 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', () => {