-
Notifications
You must be signed in to change notification settings - Fork 27.1k
fix(compiler-cli): various fixes to input transform type emitting #52437
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,11 +9,12 @@ | |
| import * as o from '@angular/compiler'; | ||
| import ts from 'typescript'; | ||
|
|
||
| import {assertSuccessfulReferenceEmit, ImportFlags, Reference, ReferenceEmitter} from '../../imports'; | ||
| import {assertSuccessfulReferenceEmit, ImportFlags, OwningModule, Reference, ReferenceEmitter} from '../../imports'; | ||
| import {ReflectionHost} from '../../reflection'; | ||
|
|
||
| import {Context} from './context'; | ||
| import {ImportManager} from './import_manager'; | ||
| import {TypeEmitter} from './type_emitter'; | ||
|
|
||
|
|
||
| export function translateType( | ||
|
|
@@ -79,12 +80,17 @@ class TypeTranslatorVisitor implements o.ExpressionVisitor, o.TypeVisitor { | |
| return ts.factory.createTypeLiteralNode([indexSignature]); | ||
| } | ||
|
|
||
| visitTransplantedType(ast: o.TransplantedType<ts.Node>, context: any) { | ||
| if (!ts.isTypeNode(ast.type)) { | ||
| visitTransplantedType(ast: o.TransplantedType<unknown>, context: Context) { | ||
| const node = ast.type instanceof Reference ? ast.type.node : ast.type; | ||
| if (!ts.isTypeNode(node)) { | ||
| throw new Error(`A TransplantedType must wrap a TypeNode`); | ||
| } | ||
|
|
||
| return this.translateTransplantedTypeNode(ast.type, context); | ||
| const viaModule = ast.type instanceof Reference ? ast.type.bestGuessOwningModule : null; | ||
|
|
||
| const emitter = | ||
| new TypeEmitter(typeRef => this.translateTypeReference(typeRef, context, viaModule)); | ||
| return emitter.emitType(node); | ||
| } | ||
|
|
||
| visitReadVarExpr(ast: o.ReadVarExpr, context: Context): ts.TypeQueryNode { | ||
|
|
@@ -253,70 +259,36 @@ class TypeTranslatorVisitor implements o.ExpressionVisitor, o.TypeVisitor { | |
| return typeNode; | ||
| } | ||
|
|
||
| /** | ||
| * Translates a type reference node so that all of its references | ||
| * are imported into the context file. | ||
| */ | ||
| private translateTransplantedTypeReferenceNode( | ||
| node: ts.TypeReferenceNode&{typeName: ts.Identifier}, context: any): ts.TypeReferenceNode { | ||
| const declaration = this.reflector.getDeclarationOfIdentifier(node.typeName); | ||
|
|
||
| private translateTypeReference( | ||
| type: ts.TypeReferenceNode, context: Context, | ||
| viaModule: OwningModule|null): ts.TypeReferenceNode|null { | ||
| const target = ts.isIdentifier(type.typeName) ? type.typeName : type.typeName.right; | ||
| const declaration = this.reflector.getDeclarationOfIdentifier(target); | ||
| if (declaration === null) { | ||
| throw new Error( | ||
| `Unable to statically determine the declaration file of type node ${node.typeName.text}`); | ||
| `Unable to statically determine the declaration file of type node ${target.text}`); | ||
| } | ||
|
|
||
| const emittedType = this.refEmitter.emit( | ||
| new Reference(declaration.node), this.contextFile, | ||
| ImportFlags.NoAliasing | ImportFlags.AllowTypeImports | | ||
| ImportFlags.AllowRelativeDtsImports); | ||
|
|
||
| assertSuccessfulReferenceEmit(emittedType, node, 'type'); | ||
|
|
||
| const result = emittedType.expression.visitExpression(this, context); | ||
|
|
||
| if (!ts.isTypeReferenceNode(result)) { | ||
| throw new Error(`Expected TypeReferenceNode when referencing the type for ${ | ||
| node.typeName.text}, but received ${ts.SyntaxKind[result.kind]}`); | ||
| let owningModule = viaModule; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can reduce this to:
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would do the same indeed, but I find the current logic more readable. |
||
| if (declaration.viaModule !== null) { | ||
| owningModule = { | ||
| specifier: declaration.viaModule, | ||
| resolutionContext: type.getSourceFile().fileName, | ||
| }; | ||
| } | ||
|
|
||
| // If the original node doesn't have any generic parameters we return the results. | ||
| if (node.typeArguments === undefined || node.typeArguments.length === 0) { | ||
| return result; | ||
| } | ||
| const reference = new Reference(declaration.node, owningModule); | ||
| const emittedType = this.refEmitter.emit( | ||
| reference, this.contextFile, ImportFlags.NoAliasing | ImportFlags.AllowTypeImports); | ||
|
|
||
| // If there are any generics, we have to reflect them as well. | ||
| const translatedArgs = | ||
| node.typeArguments.map(arg => this.translateTransplantedTypeNode(arg, context)); | ||
|
|
||
| return ts.factory.updateTypeReferenceNode( | ||
| result, result.typeName, ts.factory.createNodeArray(translatedArgs)); | ||
| } | ||
|
|
||
| /** | ||
| * Translates a type node so that all of the type references it | ||
| * contains are imported and can be referenced in the context file. | ||
| */ | ||
| private translateTransplantedTypeNode(rootNode: ts.TypeNode, context: any): ts.TypeNode { | ||
| const factory: ts.TransformerFactory<ts.Node> = transformContext => root => { | ||
| const walk = (node: ts.Node): ts.Node => { | ||
| if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName)) { | ||
| const translated = | ||
| this.translateTransplantedTypeReferenceNode(node as ts.TypeReferenceNode & { | ||
| typeName: ts.Identifier; | ||
| }, context); | ||
|
|
||
| if (translated !== node) { | ||
| return translated; | ||
| } | ||
| } | ||
|
|
||
| return ts.visitEachChild(node, walk, transformContext); | ||
| }; | ||
| assertSuccessfulReferenceEmit(emittedType, target, 'type'); | ||
|
|
||
| return ts.visitNode(root, walk); | ||
| }; | ||
| const typeNode = this.translateExpression(emittedType.expression, context); | ||
|
|
||
| return ts.transform(rootNode, [factory]).transformed[0] as ts.TypeNode; | ||
| if (!ts.isTypeReferenceNode(typeNode)) { | ||
| throw new Error( | ||
| `Expected TypeReferenceNode for emitted reference, got ${ts.SyntaxKind[typeNode.kind]}.`); | ||
| } | ||
| return typeNode; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -150,7 +150,7 @@ function constructTypeCtorParameter( | |
| /* type */ | ||
| transform == null ? | ||
| tsCreateTypeQueryForCoercedInput(rawType.typeName, classPropertyName) : | ||
| transform.type)); | ||
| transform.type.node)); | ||
|
||
| } | ||
| } | ||
| if (plainKeys.length > 0) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: would it make sense to use something like
ts.Node | Referencehere?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided not to, because the
TofTransplantedTypeis not constrained so usingts.Node | Referencehere might not be accurate.