diff --git a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts index cf01fe5278..793a27c4d6 100644 --- a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts +++ b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts @@ -160,6 +160,32 @@ describe('ngInjectableDef Bazel Integration', () => { expect(() => TestBed.inject(ChildService).value).toThrowError(/ChildService/); }); + it('uses legacy `ngInjectable` property even if it inherits from a class that has `ɵprov` property', + () => { + @Injectable({ + providedIn: 'root', + useValue: new ParentService('parent'), + }) + class ParentService { + constructor(public value: string) {} + } + + // ChildServices exteds ParentService but does not have @Injectable + class ChildService extends ParentService { + constructor(value: string) { super(value); } + static ngInjectableDef = { + providedIn: 'root', + factory: () => new ChildService('child'), + token: ChildService, + }; + } + + TestBed.configureTestingModule({}); + // We are asserting that system throws an error, rather than taking the inherited + // annotation. + expect(TestBed.inject(ChildService).value).toEqual('child'); + }); + it('NgModule injector understands requests for INJECTABLE', () => { TestBed.configureTestingModule({ providers: [{provide: 'foo', useValue: 'bar'}], diff --git a/packages/core/src/di/interface/defs.ts b/packages/core/src/di/interface/defs.ts index ac99be13a0..7a6662371b 100644 --- a/packages/core/src/di/interface/defs.ts +++ b/packages/core/src/di/interface/defs.ts @@ -190,14 +190,22 @@ export function ɵɵdefineInjector(options: {factory: () => any, providers?: any * @param type A type which may have its own (non-inherited) `ɵprov`. */ export function getInjectableDef(type: any): ɵɵInjectableDef|null { - const def = (type[NG_PROV_DEF] || type[NG_INJECTABLE_DEF]) as ɵɵInjectableDef; - // The definition read above may come from a base class. `hasOwnProperty` is not sufficient to - // distinguish this case, as in older browsers (e.g. IE10) static property inheritance is - // implemented by copying the properties. - // - // Instead, the ɵprov's token is compared to the type, and if they don't match then the - // property was not defined directly on the type itself, and was likely inherited. The definition - // is only returned if the type matches the def.token. + return getOwnDefinition(type, type[NG_PROV_DEF]) || + getOwnDefinition(type, type[NG_INJECTABLE_DEF]); +} + +/** + * Return `def` only if it is defined directly on `type` and is not inherited from a base + * class of `type`. + * + * The function `Object.hasOwnProperty` is not sufficient to distinguish this case because in older + * browsers (e.g. IE10) static property inheritance is implemented by copying the properties. + * + * Instead, the definition's `token` is compared to the `type`, and if they don't match then the + * property was not defined directly on the type itself, and was likely inherited. The definition + * is only returned if the `type` matches the `def.token`. + */ +function getOwnDefinition(type: any, def: ɵɵInjectableDef): ɵɵInjectableDef|null { return def && def.token === type ? def : null; } diff --git a/packages/core/test/bundling/injection/bundle.golden_symbols.json b/packages/core/test/bundling/injection/bundle.golden_symbols.json index 52891d4105..76e6fcdb57 100644 --- a/packages/core/test/bundling/injection/bundle.golden_symbols.json +++ b/packages/core/test/bundling/injection/bundle.golden_symbols.json @@ -131,6 +131,9 @@ { "name": "getNullInjector" }, + { + "name": "getOwnDefinition" + }, { "name": "getUndecoratedInjectableFactory" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index cb14f93b75..de1cb1a2db 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -737,6 +737,9 @@ { "name": "getOriginalError" }, + { + "name": "getOwnDefinition" + }, { "name": "getParentInjectorIndex" },