fix(ivy): ngtsc should include generic types on injectable definitions (#27037)
Analogously to directives, the `ngInjectableDef` field in .d.ts files is annotated with the type of service that it represents. If the service contains required generic type arguments, these must be included in the .d.ts file. PR Close #27037
This commit is contained in:
@ -74,6 +74,7 @@ function extractInjectableMetadata(
|
||||
const name = clazz.name.text;
|
||||
const type = new WrappedNodeExpr(clazz.name);
|
||||
const ctorDeps = getConstructorDependencies(clazz, reflector, isCore);
|
||||
const typeArgumentCount = reflector.getGenericArityOfClass(clazz) || 0;
|
||||
if (decorator.args === null) {
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.DECORATOR_NOT_CALLED, decorator.node, '@Injectable must be called');
|
||||
@ -82,6 +83,7 @@ function extractInjectableMetadata(
|
||||
return {
|
||||
name,
|
||||
type,
|
||||
typeArgumentCount,
|
||||
providedIn: new LiteralExpr(null), ctorDeps,
|
||||
};
|
||||
} else if (decorator.args.length === 1) {
|
||||
@ -118,6 +120,7 @@ function extractInjectableMetadata(
|
||||
return {
|
||||
name,
|
||||
type,
|
||||
typeArgumentCount,
|
||||
ctorDeps,
|
||||
providedIn,
|
||||
useValue: new WrappedNodeExpr(meta.get('useValue') !)
|
||||
@ -126,6 +129,7 @@ function extractInjectableMetadata(
|
||||
return {
|
||||
name,
|
||||
type,
|
||||
typeArgumentCount,
|
||||
ctorDeps,
|
||||
providedIn,
|
||||
useExisting: new WrappedNodeExpr(meta.get('useExisting') !)
|
||||
@ -134,6 +138,7 @@ function extractInjectableMetadata(
|
||||
return {
|
||||
name,
|
||||
type,
|
||||
typeArgumentCount,
|
||||
ctorDeps,
|
||||
providedIn,
|
||||
useClass: new WrappedNodeExpr(meta.get('useClass') !), userDeps
|
||||
@ -141,9 +146,9 @@ function extractInjectableMetadata(
|
||||
} else if (meta.has('useFactory')) {
|
||||
// useFactory is special - the 'deps' property must be analyzed.
|
||||
const factory = new WrappedNodeExpr(meta.get('useFactory') !);
|
||||
return {name, type, providedIn, useFactory: factory, ctorDeps, userDeps};
|
||||
return {name, type, typeArgumentCount, providedIn, useFactory: factory, ctorDeps, userDeps};
|
||||
} else {
|
||||
return {name, type, providedIn, ctorDeps};
|
||||
return {name, type, typeArgumentCount, providedIn, ctorDeps};
|
||||
}
|
||||
} else {
|
||||
throw new FatalDiagnosticError(
|
||||
|
@ -46,6 +46,24 @@ describe('ngtsc behavioral tests', () => {
|
||||
expect(dtsContents).toContain('static ngInjectableDef: i0.ɵInjectableDef<Service>;');
|
||||
});
|
||||
|
||||
it('should compile Injectables with a generic service', () => {
|
||||
env.tsconfig();
|
||||
env.write('test.ts', `
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class Store<T> {}
|
||||
`);
|
||||
|
||||
env.driveMain();
|
||||
|
||||
|
||||
const jsContents = env.getContents('test.js');
|
||||
expect(jsContents).toContain('Store.ngInjectableDef =');
|
||||
const dtsContents = env.getContents('test.d.ts');
|
||||
expect(dtsContents).toContain('static ngInjectableDef: i0.ɵInjectableDef<Store<any>>;');
|
||||
});
|
||||
|
||||
it('should compile Components without errors', () => {
|
||||
env.tsconfig();
|
||||
env.write('test.ts', `
|
||||
|
Reference in New Issue
Block a user