mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
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 ab061a7610)
This commit is contained in:
parent
e40d378f3e
commit
2c6781071f
3 changed files with 50 additions and 17 deletions
|
|
@ -251,14 +251,16 @@ class TypeTranslatorVisitor implements o.ExpressionVisitor, o.TypeVisitor {
|
|||
visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>, context: Context): ts.TypeNode {
|
||||
const node: ts.Node = ast.node;
|
||||
if (ts.isEntityName(node)) {
|
||||
return ts.factory.createTypeReferenceNode(node, /* typeArguments */ undefined);
|
||||
return ts.factory.createTypeReferenceNode(node);
|
||||
} else if (ts.isTypeNode(node)) {
|
||||
return node;
|
||||
} else if (ts.isLiteralExpression(node)) {
|
||||
return ts.factory.createLiteralTypeNode(node);
|
||||
} else if (ts.isTypeParameterDeclaration(node)) {
|
||||
return ts.factory.createTypeReferenceNode(node.name);
|
||||
} else {
|
||||
throw new Error(
|
||||
`Unsupported WrappedNodeExpr in TypeTranslatorVisitor: ${ts.SyntaxKind[node.kind]}`,
|
||||
`Unsupported WrappedNodeExpr in TypeTranslatorVisitor: ${ts.SyntaxKind[node.kind]} in ${node.getSourceFile()?.fileName}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,11 @@ export function adaptTypeCheckBlockMetadata(
|
|||
...adaptGenerics(
|
||||
dir.ref.node as ClassDeclaration<ts.ClassDeclaration>,
|
||||
env,
|
||||
TcbGenericContextBehavior.UseEmitter,
|
||||
// The directive that we're processing is its own dependency
|
||||
// so we should the same generic context behavior.
|
||||
extractRef(dir.ref).key === extractRef(ref).key
|
||||
? genericContextBehavior
|
||||
: TcbGenericContextBehavior.UseEmitter,
|
||||
),
|
||||
};
|
||||
|
||||
|
|
@ -201,13 +205,7 @@ export function adaptTypeCheckBlockMetadata(
|
|||
},
|
||||
component: {
|
||||
ref: extractRef(ref as Reference<ClassDeclaration>),
|
||||
...adaptGenerics(
|
||||
ref.node,
|
||||
env,
|
||||
env.config.useContextGenericType
|
||||
? genericContextBehavior
|
||||
: TcbGenericContextBehavior.FallbackToAny,
|
||||
),
|
||||
...adaptGenerics(ref.node, env, genericContextBehavior),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -224,6 +222,10 @@ function adaptGenerics(
|
|||
let typeArguments: string[] | null;
|
||||
|
||||
if (node.typeParameters !== undefined && node.typeParameters.length > 0) {
|
||||
if (!env.config.useContextGenericType) {
|
||||
genericContextBehavior = TcbGenericContextBehavior.FallbackToAny;
|
||||
}
|
||||
|
||||
switch (genericContextBehavior) {
|
||||
case TcbGenericContextBehavior.UseEmitter:
|
||||
const emitter = new TypeParameterEmitter(node.typeParameters, env.reflector);
|
||||
|
|
|
|||
|
|
@ -10788,10 +10788,12 @@ runInEachFileSystem((os: string) => {
|
|||
expect(codes).toEqual([ngErrorCode(ErrorCode.NGMODULE_BOOTSTRAP_IS_STANDALONE)]);
|
||||
});
|
||||
|
||||
it('should compile a component with a complex generic', () => {
|
||||
env.write(
|
||||
'test.ts',
|
||||
`
|
||||
[true, false].forEach((strictTemplates) => {
|
||||
it(`[strictTemplates: ${strictTemplates}] should compile a component with a complex generic`, () => {
|
||||
env.tsconfig({strictTemplates});
|
||||
env.write(
|
||||
'test.ts',
|
||||
`
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
|
|
@ -10803,10 +10805,37 @@ runInEachFileSystem((os: string) => {
|
|||
TOptions extends { [K in keyof T]?: T[K] } = object
|
||||
> {}
|
||||
`,
|
||||
);
|
||||
);
|
||||
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.length).toBe(0);
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.length).toBe(0);
|
||||
});
|
||||
|
||||
// See #67704.
|
||||
it(`[strictTemplates: ${strictTemplates}] should compile a directive with a generic that has type parameters`, () => {
|
||||
env.tsconfig({strictTemplates});
|
||||
env.write(
|
||||
'test.ts',
|
||||
`
|
||||
import {Directive} from '@angular/core';
|
||||
|
||||
type Foo<T> = {prop: T};
|
||||
|
||||
@Directive({
|
||||
host: {
|
||||
'[class.some-class]': 'foo || bar' // Only necessary to enable type checking.
|
||||
},
|
||||
})
|
||||
export class TestDir<T, U = T extends Foo<infer V> ? V : never> {
|
||||
foo?: T;
|
||||
bar?: U;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('InjectorDef emit optimizations for standalone', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue