Skip to content

Commit 2c67810

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. (cherry picked from commit ab061a7)
1 parent e40d378 commit 2c67810

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
@@ -121,7 +121,11 @@ export function adaptTypeCheckBlockMetadata(
121121
...adaptGenerics(
122122
dir.ref.node as ClassDeclaration<ts.ClassDeclaration>,
123123
env,
124-
TcbGenericContextBehavior.UseEmitter,
124+
// The directive that we're processing is its own dependency
125+
// so we should the same generic context behavior.
126+
extractRef(dir.ref).key === extractRef(ref).key
127+
? genericContextBehavior
128+
: TcbGenericContextBehavior.UseEmitter,
125129
),
126130
};
127131

@@ -201,13 +205,7 @@ export function adaptTypeCheckBlockMetadata(
201205
},
202206
component: {
203207
ref: extractRef(ref as Reference<ClassDeclaration>),
204-
...adaptGenerics(
205-
ref.node,
206-
env,
207-
env.config.useContextGenericType
208-
? genericContextBehavior
209-
: TcbGenericContextBehavior.FallbackToAny,
210-
),
208+
...adaptGenerics(ref.node, env, genericContextBehavior),
211209
},
212210
};
213211
}
@@ -224,6 +222,10 @@ function adaptGenerics(
224222
let typeArguments: string[] | null;
225223

226224
if (node.typeParameters !== undefined && node.typeParameters.length > 0) {
225+
if (!env.config.useContextGenericType) {
226+
genericContextBehavior = TcbGenericContextBehavior.FallbackToAny;
227+
}
228+
227229
switch (genericContextBehavior) {
228230
case TcbGenericContextBehavior.UseEmitter:
229231
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
@@ -10788,10 +10788,12 @@ runInEachFileSystem((os: string) => {
1078810788
expect(codes).toEqual([ngErrorCode(ErrorCode.NGMODULE_BOOTSTRAP_IS_STANDALONE)]);
1078910789
});
1079010790

10791-
it('should compile a component with a complex generic', () => {
10792-
env.write(
10793-
'test.ts',
10794-
`
10791+
[true, false].forEach((strictTemplates) => {
10792+
it(`[strictTemplates: ${strictTemplates}] should compile a component with a complex generic`, () => {
10793+
env.tsconfig({strictTemplates});
10794+
env.write(
10795+
'test.ts',
10796+
`
1079510797
import {Component} from '@angular/core';
1079610798
1079710799
@Component({
@@ -10803,10 +10805,37 @@ runInEachFileSystem((os: string) => {
1080310805
TOptions extends { [K in keyof T]?: T[K] } = object
1080410806
> {}
1080510807
`,
10806-
);
10808+
);
1080710809

10808-
const diags = env.driveDiagnostics();
10809-
expect(diags.length).toBe(0);
10810+
const diags = env.driveDiagnostics();
10811+
expect(diags.length).toBe(0);
10812+
});
10813+
10814+
// See #67704.
10815+
it(`[strictTemplates: ${strictTemplates}] should compile a directive with a generic that has type parameters`, () => {
10816+
env.tsconfig({strictTemplates});
10817+
env.write(
10818+
'test.ts',
10819+
`
10820+
import {Directive} from '@angular/core';
10821+
10822+
type Foo<T> = {prop: T};
10823+
10824+
@Directive({
10825+
host: {
10826+
'[class.some-class]': 'foo || bar' // Only necessary to enable type checking.
10827+
},
10828+
})
10829+
export class TestDir<T, U = T extends Foo<infer V> ? V : never> {
10830+
foo?: T;
10831+
bar?: U;
10832+
}
10833+
`,
10834+
);
10835+
10836+
const diags = env.driveDiagnostics();
10837+
expect(diags.length).toBe(0);
10838+
});
1081010839
});
1081110840

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

0 commit comments

Comments
 (0)