diff --git a/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts b/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts index 19ebb420746..4f66f1e7e69 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/api/api.ts @@ -8,22 +8,11 @@ import { AbsoluteSourceSpan, - AST, BoundTarget, DirectiveMeta, - DirectiveOwner, LegacyAnimationTriggerNames, ParseSourceSpan, SchemaMetadata, - TemplateEntity, - TmplAstBoundAttribute, - TmplAstBoundEvent, - TmplAstComponent, - TmplAstDirective, - TmplAstElement, - TmplAstReference, - TmplAstTemplate, - TmplAstTextAttribute, } from '@angular/compiler'; import ts from 'typescript'; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts index a80f2930012..9666de22fbb 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/environment.ts @@ -8,14 +8,9 @@ import ts from 'typescript'; -import { - assertSuccessfulReferenceEmit, - ImportFlags, - Reference, - ReferenceEmitter, -} from '../../imports'; -import {ClassDeclaration, ReflectionHost} from '../../reflection'; -import {ImportManager, translateExpression} from '../../translator'; +import {ReferenceEmitter} from '../../imports'; +import {ReflectionHost} from '../../reflection'; +import {ImportManager} from '../../translator'; import { TcbDirectiveMetadata, TcbPipeMetadata, @@ -26,9 +21,8 @@ import { } from '../api'; import {ReferenceEmitEnvironment} from './reference_emit_environment'; -import {generateTypeCtorDeclarationFn, requiresInlineTypeCtor} from './type_constructor'; -import {TypeParameterEmitter} from './type_parameter_emitter'; -import {declareVariable, TcbExpr, tempPrint} from './ops/codegen'; +import {generateTypeCtorDeclarationFn} from './type_constructor'; +import {declareVariable, TcbExpr} from './ops/codegen'; /** * A context which hosts one or more Type Check Blocks (TCBs). @@ -83,10 +77,7 @@ export class Environment extends ReferenceEmitEnvironment { return new TcbExpr(typeCtorExpr); } else { const fnName = `_ctor${this.nextIds.typeCtor++}`; - const nodeTypeRef = this.referenceTcbType(dir.ref); - if (!ts.isTypeReferenceNode(nodeTypeRef)) { - throw new Error(`Expected TypeReferenceNode from reference to ${dir.ref.name}`); - } + const nodeTypeRef = this.referenceTcbValue(dir.ref); const meta: TypeCtorMetadata = { fnName, body: true, @@ -98,7 +89,7 @@ export class Environment extends ReferenceEmitEnvironment { }; const typeParams = dir.typeParameters || undefined; - const typeCtor = generateTypeCtorDeclarationFn(this, meta, nodeTypeRef.typeName, typeParams); + const typeCtor = generateTypeCtorDeclarationFn(this, meta, nodeTypeRef, typeParams); this.typeCtorStatements.push(typeCtor); this.typeCtors.set(key, fnName); return new TcbExpr(fnName); @@ -114,46 +105,14 @@ export class Environment extends ReferenceEmitEnvironment { return new TcbExpr(this.pipeInsts.get(key)!); } - const pipeType = this.referenceTcbType(pipe.ref); + const pipeType = this.referenceTcbValue(pipe.ref); const pipeInstId = `_pipe${this.nextIds.pipeInst++}`; this.pipeInsts.set(key, pipeInstId); - this.pipeInstStatements.push( - declareVariable(new TcbExpr(pipeInstId), new TcbExpr(tempPrint(pipeType, this.contextFile))), - ); + this.pipeInstStatements.push(declareVariable(new TcbExpr(pipeInstId), pipeType)); return new TcbExpr(pipeInstId); } - /** - * Generate a `ts.Expression` that references the given node. - * - * This may involve importing the node into the file if it's not declared there already. - */ - reference(ref: Reference>): TcbExpr { - // Disable aliasing for imports generated in a template type-checking context, as there is no - // guarantee that any alias re-exports exist in the .d.ts files. It's safe to use direct imports - // in these cases as there is no strict dependency checking during the template type-checking - // pass. - const ngExpr = this.refEmitter.emit(ref, this.contextFile, ImportFlags.NoAliasing); - assertSuccessfulReferenceEmit(ngExpr, this.contextFile, 'class'); - - // Use `translateExpression` to convert the `Expression` into a `ts.Expression`. - const tsExpression = translateExpression( - this.contextFile, - ngExpr.expression, - this.importManager, - ); - - return new TcbExpr(tempPrint(tsExpression, this.contextFile)); - } - - private emitTypeParameters( - declaration: ClassDeclaration, - ): ts.TypeParameterDeclaration[] | undefined { - const emitter = new TypeParameterEmitter(declaration.typeParameters, this.reflector); - return emitter.emit((ref) => this.referenceType(ref)); - } - getPreludeStatements(): TcbExpr[] { return [...this.pipeInstStatements, ...this.typeCtorStatements]; } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/ops/directive_type.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/ops/directive_type.ts index 13fe67597e1..83a4f670474 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/ops/directive_type.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/ops/directive_type.ts @@ -7,11 +7,10 @@ */ import {DirectiveOwner, ParseSourceSpan, TmplAstHostElement} from '@angular/compiler'; -import ts from 'typescript'; import type {Context} from './context'; import type {Scope} from './scope'; import {TcbOp} from './base'; -import {declareVariable, TcbExpr, tempPrint} from './codegen'; +import {declareVariable, TcbExpr} from './codegen'; import {TcbDirectiveMetadata} from '../../api'; import {ExpressionIdentifier} from '../comments'; @@ -37,7 +36,7 @@ export abstract class TcbDirectiveTypeOpBase extends TcbOp { } override execute(): TcbExpr { - const rawType = this.tcb.env.referenceTcbType(this.dir.ref); + const rawType = this.tcb.env.referenceTcbValue(this.dir.ref); let type: TcbExpr; let span: ParseSourceSpan; @@ -46,20 +45,12 @@ export abstract class TcbDirectiveTypeOpBase extends TcbOp { this.dir.typeParameters === null || this.dir.typeParameters.length === 0 ) { - type = new TcbExpr(tempPrint(rawType, this.tcb.env.contextFile)); + type = rawType; } else { - if (!ts.isTypeReferenceNode(rawType)) { - throw new Error( - `Expected TypeReferenceNode when referencing the type for ${this.dir.ref.name}`, - ); - } - const typeName = ts.isIdentifier(rawType.typeName) - ? rawType.typeName.text - : tempPrint(rawType.typeName, this.tcb.env.contextFile); const typeArguments = Array(this.dir.typeParameters?.length ?? 0) .fill('any') .join(', '); - type = new TcbExpr(`${typeName}<${typeArguments}>`); + type = new TcbExpr(`${rawType.print()}<${typeArguments}>`); } if (this.node instanceof TmplAstHostElement) { diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/ops/inputs.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/ops/inputs.ts index dacca8f425c..da838ec718d 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/ops/inputs.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/ops/inputs.ts @@ -15,14 +15,12 @@ import { TmplAstDirective, TmplAstElement, TmplAstTemplate, - TransplantedType, } from '@angular/compiler'; -import ts from 'typescript'; import type {Context} from './context'; import type {Scope} from './scope'; import {TcbDirectiveMetadata} from '../../api'; import {TcbOp} from './base'; -import {declareVariable, quoteAndEscape, TcbExpr, tempPrint} from './codegen'; +import {declareVariable, quoteAndEscape, TcbExpr} from './codegen'; import {BindingPropertyName, ClassPropertyName} from '../../../metadata'; import {REGISTRY} from '../dom'; import {tcbExpression, unwrapWritableSignal} from './expression'; @@ -125,17 +123,8 @@ export class TcbDirectiveInputsOp extends TcbOp { // expression into the input field directly. To achieve this, a variable is declared // with a type of `typeof Directive.ngAcceptInputType_fieldName` which is then used as // target of the assignment. - const dirTypeRef: ts.TypeNode = this.tcb.env.referenceTcbType(this.dir.ref); - - if (!ts.isTypeReferenceNode(dirTypeRef)) { - throw new Error(`Expected TypeReferenceNode from reference to ${this.dir.ref.name}`); - } - - const typeName = ts.isIdentifier(dirTypeRef.typeName) - ? dirTypeRef.typeName.text - : tempPrint(dirTypeRef.typeName, dirTypeRef.typeName.getSourceFile()); - - type = new TcbExpr(`typeof ${typeName}.ngAcceptInputType_${fieldName}`); + const dirTypeRef = this.tcb.env.referenceTcbValue(this.dir.ref); + type = new TcbExpr(`typeof ${dirTypeRef.print()}.ngAcceptInputType_${fieldName}`); } const id = new TcbExpr(this.tcb.allocateId()); @@ -160,10 +149,6 @@ export class TcbDirectiveInputsOp extends TcbOp { } const id = new TcbExpr(this.tcb.allocateId()); - const dirTypeRef = this.tcb.env.referenceTcbType(this.dir.ref); - if (!ts.isTypeReferenceNode(dirTypeRef)) { - throw new Error(`Expected TypeReferenceNode from reference to ${this.dir.ref.name}`); - } const type = new TcbExpr(`(typeof ${dirId.print()})[${quoteAndEscape(fieldName)}]`); const temp = declareVariable(id, type); this.scope.addStatement(temp); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/ops/template.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/ops/template.ts index 11af1da7166..513bf68de5d 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/ops/template.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/ops/template.ts @@ -6,14 +6,11 @@ * found in the LICENSE file at https://angular.dev/license */ import {TmplAstBoundAttribute, TmplAstDirective, TmplAstTemplate} from '@angular/compiler'; -import ts from 'typescript'; import {TcbOp} from './base'; import {declareVariable, getStatementsBlock, TcbExpr} from './codegen'; import type {Context} from './context'; import type {Scope} from './scope'; import {TcbDirectiveMetadata} from '../../api'; -import {Reference} from '../../../imports'; -import {ClassDeclaration} from '../../../reflection'; import {tcbExpression} from './expression'; /** diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/reference_emit_environment.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/reference_emit_environment.ts index 91d40c8c433..96127e34ff1 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/reference_emit_environment.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/reference_emit_environment.ts @@ -6,13 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import { - ExpressionType, - ExternalExpr, - TransplantedType, - Type, - TypeModifier, -} from '@angular/compiler'; +import {ExpressionType, TransplantedType} from '@angular/compiler'; import ts from 'typescript'; import {ErrorCode, FatalDiagnosticError, makeDiagnosticChain} from '../../diagnostics'; @@ -77,33 +71,6 @@ export class ReferenceEmitEnvironment { ); } - /** - * Generates a `ts.TypeNode` from a `TcbReferenceMetadata` object. - * This is used by the TCB operations which do not hold on to the original `ts.Declaration`. - * - * Note: It's important that we do not try to evaluate the `typeParameters` here and pad them - * out with `any` type arguments. If we supply `any` to a generic pipe (e.g. `var _pipe1: MyPipe;`), - * it destroys the generic constraints and degrades the `transform` signature. When they are omitted - * entirely, TypeScript implicitly flags an error, which the Angular compiler filters out, and - * crucially recovers by falling back to constraint inference (e.g. `var _pipe1: MyPipe;` infers - * bounds safely). - */ - referenceTcbType(ref: TcbReferenceMetadata): ts.TypeNode { - if (ref.unexportedDiagnostic !== null || ref.isLocal || ref.moduleName === null) { - if (ref.unexportedDiagnostic !== null) { - throw new FatalDiagnosticError( - ErrorCode.IMPORT_GENERATION_FAILURE, - this.contextFile, // Using context file as fallback origin for external file since we lack exact node - makeDiagnosticChain(`Unable to import symbol ${ref.name}.`, [ - makeDiagnosticChain(ref.unexportedDiagnostic), - ]), - ); - } - return ts.factory.createTypeReferenceNode(ref.name); - } - return this.referenceExternalType(ref.moduleName, ref.name); - } - /** * Generates a `TcbExpr` from a `TcbReferenceMetadata` object. */ @@ -139,23 +106,6 @@ export class ReferenceEmitEnvironment { throw new Error('Unexpected value returned by import manager'); } - /** - * Generate a `ts.TypeNode` that references a given type from the provided module. - * - * This will involve importing the type into the file, and will also add type parameters if - * provided. - */ - referenceExternalType(moduleName: string, name: string, typeParams?: Type[]): ts.TypeNode { - const external = new ExternalExpr({moduleName, name}); - return translateType( - new ExpressionType(external, TypeModifier.None, typeParams), - this.contextFile, - this.reflector, - this.refEmitter, - this.importManager, - ); - } - /** * Generates a `ts.TypeNode` representing a type that is being referenced from a different place * in the program. Any type references inside the transplanted type will be rewritten so that diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/tcb_adapter.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/tcb_adapter.ts index 122bb57b791..ed6f56c43d9 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/tcb_adapter.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/tcb_adapter.ts @@ -15,7 +15,6 @@ import { TcbInputMapping, TcbPipeMetadata, TypeCheckableDirectiveMeta, - TcbTypeParameter, } from '../api'; import {Environment} from './environment'; import {ImportFlags, ReferenceEmitKind, Reference} from '../../imports'; @@ -24,9 +23,7 @@ import { ExternalExpr, TransplantedType, BoundTarget, - DirectiveOwner, ReferenceTarget, - TmplAstReference, TmplAstElement, TmplAstTemplate, WrappedNodeExpr, diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts index 4d274932a01..a776235e323 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts @@ -7,21 +7,14 @@ */ import ts from 'typescript'; - -import { - TcbComponentMetadata, - TcbTypeCheckBlockMetadata, - TcbTypeParameter, - TypeCheckId, -} from '../api'; - +import {TcbComponentMetadata, TcbTypeCheckBlockMetadata, TcbTypeParameter} from '../api'; import {DomSchemaChecker} from './dom'; import {Environment} from './environment'; import {OutOfBandDiagnosticRecorder} from './oob'; import {createHostBindingsBlockGuard} from './host_bindings'; import {Context, TcbGenericContextBehavior} from './ops/context'; import {Scope} from './ops/scope'; -import {getStatementsBlock, tempPrint} from './ops/codegen'; +import {getStatementsBlock} from './ops/codegen'; /** * Given a `ts.ClassDeclaration` for a component, and metadata regarding that component, compose a @@ -67,13 +60,7 @@ export function generateTypeCheckBlock( meta.isStandalone, meta.preserveWhitespaces, ); - const ctxRawType = env.referenceTcbType(component.ref); - if (!ts.isTypeReferenceNode(ctxRawType)) { - throw new Error( - `Expected TypeReferenceNode when referencing the ctx param for ${component.ref.name}`, - ); - } - + const ctxRawType = env.referenceTcbValue(component.ref); let typeParameters: TcbTypeParameter[] | undefined = undefined; let typeArguments: string[] | undefined = undefined; @@ -102,7 +89,6 @@ export function generateTypeCheckBlock( } } - const sourceFile = env.contextFile; const typeParamsStr = typeParameters === undefined || typeParameters.length === 0 ? '' @@ -111,11 +97,8 @@ export function generateTypeCheckBlock( typeArguments === undefined || typeArguments.length === 0 ? '' : `<${typeArguments.join(', ')}>`; - const typeRef = ts.isIdentifier(ctxRawType.typeName) - ? ctxRawType.typeName.text - : tempPrint(ctxRawType.typeName, sourceFile); - const thisParamStr = `this: ${typeRef}${typeArgsStr}`; + const thisParamStr = `this: ${ctxRawType.print()}${typeArgsStr}`; const statements: string[] = []; // Add the template type checking code. diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_constructor.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_constructor.ts index 7392d7e4a31..4fbd30abfb7 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_constructor.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_constructor.ts @@ -14,20 +14,17 @@ import {TypeCtorMetadata, TcbTypeParameter} from '../api'; import {ReferenceEmitEnvironment} from './reference_emit_environment'; import {checkIfGenericTypeBoundsCanBeEmitted, generateTcbTypeParameters} from './tcb_util'; -import {quoteAndEscape, TcbExpr, tempPrint} from './ops/codegen'; +import {quoteAndEscape, TcbExpr} from './ops/codegen'; export function generateTypeCtorDeclarationFn( env: ReferenceEmitEnvironment, meta: TypeCtorMetadata, - nodeTypeRef: ts.EntityName, + nodeTypeRef: TcbExpr, typeParams: TcbTypeParameter[] | undefined, ): TcbExpr { const typeArgs = generateGenericArgs(typeParams); - const typeRef = ts.isIdentifier(nodeTypeRef) - ? nodeTypeRef.text - : tempPrint(nodeTypeRef, nodeTypeRef.getSourceFile()); - const typeRefWithGenerics = `${typeRef}${typeArgs}`; - const initParam = constructTypeCtorParameter(env, meta, typeRef, typeRefWithGenerics); + const typeRefWithGenerics = `${nodeTypeRef.print()}${typeArgs}`; + const initParam = constructTypeCtorParameter(env, meta, nodeTypeRef.print(), typeRefWithGenerics); const typeParameters = typeParametersWithDefaultTypes(typeParams); let source: string;