Skip to content

Commit ab061a7

Browse files
crisbetoatscott
authored andcommitted
fix(compiler-cli): error for type parameter declarations
Fixes an error that was heppning when a generic param has type parameters of its own. There were a few different issues going on: 1. In #67707 I had changed a bit how we pass the `genericContextBehavior` which ended up ignoring the `useContextGenericType` option from the environment. 2. All directives depend on themselves, but we were overridding the `genericContextBehavior` for the directive being processed. 3. The type translator wasn't handling type parameter declarations. Technically we shouldn't be able to hit a code path that has a type parameter, however it's also easy enough to handle so we might as well. Relates to #67704.
1 parent 2ce0e98 commit ab061a7

3 files changed

Lines changed: 50 additions & 17 deletions

File tree

packages/compiler-cli/src/ngtsc/translator/src/type_translator.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,14 +251,16 @@ class TypeTranslatorVisitor implements o.ExpressionVisitor, o.TypeVisitor {
251251
visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>, context: Context): ts.TypeNode {
252252
const node: ts.Node = ast.node;
253253
if (ts.isEntityName(node)) {
254-
return ts.factory.createTypeReferenceNode(node, /* typeArguments */ undefined);
254+
return ts.factory.createTypeReferenceNode(node);
255255
} else if (ts.isTypeNode(node)) {
256256
return node;
257257
} else if (ts.isLiteralExpression(node)) {
258258
return ts.factory.createLiteralTypeNode(node);
259+
} else if (ts.isTypeParameterDeclaration(node)) {
260+
return ts.factory.createTypeReferenceNode(node.name);
259261
} else {
260262
throw new Error(
261-
`Unsupported WrappedNodeExpr in TypeTranslatorVisitor: ${ts.SyntaxKind[node.kind]}`,
263+
`Unsupported WrappedNodeExpr in TypeTranslatorVisitor: ${ts.SyntaxKind[node.kind]} in ${node.getSourceFile()?.fileName}`,
262264
);
263265
}
264266
}

packages/compiler-cli/src/ngtsc/typecheck/src/tcb_adapter.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,11 @@ export function adaptTypeCheckBlockMetadata(
125125
...adaptGenerics(
126126
dir.ref.node as ClassDeclaration<ts.ClassDeclaration>,
127127
env,
128-
TcbGenericContextBehavior.UseEmitter,
128+
// The directive that we're processing is its own dependency
129+
// so we should the same generic context behavior.
130+
extractRef(dir.ref).key === extractRef(ref).key
131+
? genericContextBehavior
132+
: TcbGenericContextBehavior.UseEmitter,
129133
),
130134
};
131135

@@ -209,13 +213,7 @@ export function adaptTypeCheckBlockMetadata(
209213
},
210214
component: {
211215
ref: extractRef(ref as Reference<ClassDeclaration>),
212-
...adaptGenerics(
213-
ref.node,
214-
env,
215-
env.config.useContextGenericType
216-
? genericContextBehavior
217-
: TcbGenericContextBehavior.FallbackToAny,
218-
),
216+
...adaptGenerics(ref.node, env, genericContextBehavior),
219217
},
220218
};
221219
}
@@ -232,6 +230,10 @@ function adaptGenerics(
232230
let typeArguments: string[] | null;
233231

234232
if (node.typeParameters !== undefined && node.typeParameters.length > 0) {
233+
if (!env.config.useContextGenericType) {
234+
genericContextBehavior = TcbGenericContextBehavior.FallbackToAny;
235+
}
236+
235237
switch (genericContextBehavior) {
236238
case TcbGenericContextBehavior.UseEmitter:
237239
const emitter = new TypeParameterEmitter(node.typeParameters, env.reflector);

packages/compiler-cli/test/ngtsc/ngtsc_spec.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10814,10 +10814,12 @@ runInEachFileSystem((os: string) => {
1081410814
expect(codes).toEqual([ngErrorCode(ErrorCode.NGMODULE_BOOTSTRAP_IS_STANDALONE)]);
1081510815
});
1081610816

10817-
it('should compile a component with a complex generic', () => {
10818-
env.write(
10819-
'test.ts',
10820-
`
10817+
[true, false].forEach((strictTemplates) => {
10818+
it(`[strictTemplates: ${strictTemplates}] should compile a component with a complex generic`, () => {
10819+
env.tsconfig({strictTemplates});
10820+
env.write(
10821+
'test.ts',
10822+
`
1082110823
import {Component} from '@angular/core';
1082210824
1082310825
@Component({
@@ -10829,10 +10831,37 @@ runInEachFileSystem((os: string) => {
1082910831
TOptions extends { [K in keyof T]?: T[K] } = object
1083010832
> {}
1083110833
`,
10832-
);
10834+
);
1083310835

10834-
const diags = env.driveDiagnostics();
10835-
expect(diags.length).toBe(0);
10836+
const diags = env.driveDiagnostics();
10837+
expect(diags.length).toBe(0);
10838+
});
10839+
10840+
// See #67704.
10841+
it(`[strictTemplates: ${strictTemplates}] should compile a directive with a generic that has type parameters`, () => {
10842+
env.tsconfig({strictTemplates});
10843+
env.write(
10844+
'test.ts',
10845+
`
10846+
import {Directive} from '@angular/core';
10847+
10848+
type Foo<T> = {prop: T};
10849+
10850+
@Directive({
10851+
host: {
10852+
'[class.some-class]': 'foo || bar' // Only necessary to enable type checking.
10853+
},
10854+
})
10855+
export class TestDir<T, U = T extends Foo<infer V> ? V : never> {
10856+
foo?: T;
10857+
bar?: U;
10858+
}
10859+
`,
10860+
);
10861+
10862+
const diags = env.driveDiagnostics();
10863+
expect(diags.length).toBe(0);
10864+
});
1083610865
});
1083710866

1083810867
describe('InjectorDef emit optimizations for standalone', () => {

0 commit comments

Comments
 (0)