From 7912db3829fb2c028d0c40a120c19e830e63da33 Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Tue, 4 Jun 2019 13:54:57 -0700 Subject: [PATCH] fix(ivy): call factory functions with correct type for derived classes (#30855) In a derived service class with no decorator (and therefore no factory) of its own, the factory function of the base class will be used instead. Previously this logic had a bug where the factory function would be called with no arguments, which would incorrectly create an instance of the base class. This commit adds logic to call the base class' factory and pass the type of the derived class, which will correctly construct an instance of the derived class using the base class' factory. A test is also added to verify correctness of this behavior. PR Close #30855 --- packages/core/src/di/r3_injector.ts | 2 +- packages/core/test/di/r3_injector_spec.ts | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/core/src/di/r3_injector.ts b/packages/core/src/di/r3_injector.ts index 276e868117..5d7f151393 100644 --- a/packages/core/src/di/r3_injector.ts +++ b/packages/core/src/di/r3_injector.ts @@ -438,7 +438,7 @@ function getUndecoratedInjectableFactory(token: Function) { // just instantiates the zero-arg constructor. const inheritedInjectableDef = getInheritedInjectableDef(token); if (inheritedInjectableDef !== null) { - return inheritedInjectableDef.factory; + return () => inheritedInjectableDef.factory(token as Type); } else { return () => new (token as Type)(); } diff --git a/packages/core/test/di/r3_injector_spec.ts b/packages/core/test/di/r3_injector_spec.ts index 4307985b59..4bf206d3a1 100644 --- a/packages/core/test/di/r3_injector_spec.ts +++ b/packages/core/test/di/r3_injector_spec.ts @@ -62,7 +62,9 @@ describe('InjectorDef-based createInjector()', () => { static ngInjectableDef = ɵɵdefineInjectable({ token: ServiceWithDep, providedIn: null, - factory: () => new ServiceWithDep(ɵɵinject(Service)), + // ChildService is derived from ServiceWithDep, so the factory function here must do the right + // thing and create an instance of the requested type if one is given. + factory: (t?: typeof ServiceWithDep) => new (t || ServiceWithDep)(ɵɵinject(Service)), }); } @@ -163,11 +165,14 @@ describe('InjectorDef-based createInjector()', () => { }); } + class ChildService extends ServiceWithDep {} + class Module { static ngInjectorDef = ɵɵdefineInjector({ factory: () => new Module(), imports: [IntermediateModule], providers: [ + ChildService, ServiceWithDep, ServiceWithOptionalDep, ServiceWithMultiDep, @@ -359,6 +364,11 @@ describe('InjectorDef-based createInjector()', () => { expect(instance).toBe(injector.get(ScopedService)); }); + it('allows injecting an inherited service', () => { + const instance = injector.get(ChildService); + expect(instance instanceof ChildService).toBe(true); + }); + it('does not create instances of a service not in scope', () => { expect(injector.get(WrongScopeService, null)).toBeNull(); });