Create type node#14709
Conversation
…existing node factory, formatter and printer
This reverts commit fd65966. Accidentally pushed to the wrong branch.
Fix assigning positions to node array
* create type reference node from type parameters * expose index signature synthesis * widen types in helpers * format unions * use deep cloning
| visitNodes(node.modifiers, visitor, isModifier), | ||
| node.asteriskToken, | ||
| node.name, | ||
| node.questionToken, |
There was a problem hiding this comment.
questionToken is TypeScript syntax. You might as well just pass undefined here, as this property should always be undefined by the time the es2017 transform runs. We do the same for decorators, typeParameters, and type here as well.
| /*modifiers*/ undefined, | ||
| node.dotDotDotToken, | ||
| getGeneratedNameForNode(node), | ||
| node.questionToken, |
There was a problem hiding this comment.
Just pass undefined here.
| ? undefined | ||
| : node.asteriskToken, | ||
| visitNode(node.name, visitor, isPropertyName), | ||
| visitNode(node.questionToken, visitor, isToken), |
There was a problem hiding this comment.
Just pass undefined here.
| visitNodes(node.modifiers, modifierVisitor, isModifier), | ||
| node.asteriskToken, | ||
| visitPropertyNameOfClassElement(node), | ||
| node.questionToken, |
There was a problem hiding this comment.
You should pass undefined here.
| || kind === SyntaxKind.BooleanKeyword | ||
| || kind === SyntaxKind.StringKeyword | ||
| || kind === SyntaxKind.SymbolKeyword | ||
| || kind === SyntaxKind.ThisKeyword |
There was a problem hiding this comment.
Keep in mind this and null can also be expressions. I would check to be sure that isTypeNode (which calls isTypeNodeKind) isn't used to skip visiting a TypeNode that could also be an expression.
There was a problem hiding this comment.
resolved offline: we decided that conflating this as a type node and expression isn't a problem for now because it has no children and we would only rely on the distinction for now when aggregating modifier flags from children.
We may want to introduce a distinction as we have with StringLiteral and LiteralType in the future.
| // Type Elements | ||
|
|
||
| export function createConstructSignature() { | ||
| throw new Error("not implemented."); |
There was a problem hiding this comment.
Why have this if it is not implemented?
|
|
||
| export function createTypeReferenceNode(typeName: string | EntityName, typeArguments: NodeArray<TypeNode> | undefined) { | ||
| const typeReference = createSynthesizedNode(SyntaxKind.TypeReference) as TypeReferenceNode; | ||
| typeReference.typeName = isQualifiedName(<EntityName>typeName) ? <QualifiedName>typeName : asName(<string | Identifier>typeName); |
There was a problem hiding this comment.
isQualifiedName test is unnecessary, just add an overload to asName for EntityName just as we do for BindingName and PropertyName
| getNonNullableType(type: Type): Type; | ||
|
|
||
| /** Note that the resulting nodes cannot be checked. */ | ||
| createTypeNode(type: Type, enclosingDeclaration: Node): TypeNode; |
There was a problem hiding this comment.
I would recommend calling these:
typeToTypeNode and signatureToSingatureDeclaration to match typeToString and signatureToString.
I would also remove createSignatureParts, possibly extract it to a helper if needed.
| } | ||
|
|
||
| let typeString = "any"; | ||
| let typeNode: TypeNode = createKeywordTypeNode(SyntaxKind.AnyKeyword); |
There was a problem hiding this comment.
move this to use site, no need to create it all the time.
| const visibility = getVisibilityPrefixWithSpace(getModifierFlags(declaration)); | ||
|
|
||
| const type = checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration); | ||
| const name = declaration.name ? getSynthesizedDeepClone(declaration.name) as PropertyName : undefined; |
There was a problem hiding this comment.
do not think you need a clone here, just use the same node
| /*decorators*/undefined, | ||
| modifiers, | ||
| name, | ||
| /*questionToken*/ undefined, |
There was a problem hiding this comment.
you should be able to know if the parent property is optional or not, and then add the questiontoken as needed.
|
|
||
| function createTypeNodeWorker(type: Type): TypeNode { | ||
| if (!type) { | ||
| if (undefinedArgumentIsError) { encounteredError = true; } |
There was a problem hiding this comment.
i would make it always an error.
| return (<IntrinsicType>type).intrinsicName === "true" ? createTrue() : createFalse(); | ||
| } | ||
| if (type.flags & TypeFlags.EnumLiteral) { | ||
| throw new Error("enum literal not implemented"); |
There was a problem hiding this comment.
this should work, e.g. var x: E.value
There was a problem hiding this comment.
so i would remove this then.
| return createAnonymousTypeNode(<ObjectType>type); | ||
| } | ||
|
|
||
| // TODO (aozgaa): implement string and number literals here once there is a testable case. |
There was a problem hiding this comment.
these are already handled earlier.
There was a problem hiding this comment.
Do we really need this check? Are we being defensive against third parties or ourselves? If ourselves, then this check shouldn't be strictly necessary.
There was a problem hiding this comment.
Do we really need this closure? We just tear off the functions and add them to TypeChecker.
| return createKeywordTypeNode(SyntaxKind.NeverKeyword); | ||
| } | ||
| if (type.flags & TypeFlags.ESSymbol) { | ||
| throw new Error("ESSymbol not implemented"); |
There was a problem hiding this comment.
Should be createKeywordTypeNode(SyntaxKind.SymbolKeyword)?
| throw new Error("ESSymbol not implemented"); | ||
| } | ||
| if (type.flags & TypeFlags.NonPrimitive) { | ||
| throw new Error("Non primitive not implemented"); |
There was a problem hiding this comment.
Should be createKeywordTypeNode(SyntaxKind.ObjectKeyword)?
| } | ||
| }; | ||
|
|
||
| function typeToTypeNodeHelper(type: Type, enclosingDeclaration: Node, flags: NodeBuilderFlags): TypeNode { |
There was a problem hiding this comment.
Consider moving the inObjectTypeLiteral, checkAlias, and symbolStack variables to a state object and call typeToTypeNodeHelper recursively instead.
|
|
||
| function signatureToMethodDeclaration(signature: Signature, enclosingDeclaration: Node, body?: Block) { | ||
| const signatureDeclaration = <MethodDeclaration>checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration); | ||
| if (signatureDeclaration) { |
There was a problem hiding this comment.
May introduce new hidden class? Ensure decorators is assigned to undefined either here or in signatureToSignatureDeclaration.
| newSignatureDeclaration.parent = enclosingDeclaration; | ||
| newSignatureDeclaration.name = signatures[0].getDeclaration().name; | ||
| function createMethodImplementingSignatures(signatures: Signature[], name: PropertyName, optional: boolean, modifiers: Modifier[] | undefined): MethodDeclaration { | ||
| Debug.assert(signatures && signatures.length > 0); |
| if (nonRestLength > maxNonRestArgs) { | ||
| maxNonRestArgs = nonRestLength; | ||
| maxArgsIndex = i; | ||
| someSigHasRestParameter = someSigHasRestParameter || sig.hasRestParameter; |
There was a problem hiding this comment.
Unecessary assignments, consider:
if (sig.hasRestParameter) someSigHasRestParameter = true;| * for them separators are treated as the part of the node. | ||
| * This function should be used to insert nodes in lists when nodes don't carry separators as the part of the node range, | ||
| * i.e. arguments in arguments lists, parameters in parameter lists etc. | ||
| * Note that separators are art of the node in statements and class elements. |
| // order changes by start position | ||
| const normalized = stableSort(changes, (a, b) => a.range.pos - b.range.pos); | ||
| // verify that end position of the change is less than start position of the next change | ||
| // verify that change intervals to not overlap, except possible at end points. |
Migrating away from
typeToStringin codefixes, with the eventual hope of eliminatingtypeToString.While there is still work to be done, I'm putting this up now to solicit feedback.
Known issues:
typeToStringin the AST. This is most notable with type references that have type arguments in the middle of their qualified names.getSynthesizeDeepCloneis necessary (https://github.com/Microsoft/TypeScript/compare/master...aozgaa:createTypeNode?expand=1#diff-9c914fcc05c72a72db62c0a023b15f3fR71).undefinedif we enocunter an error?buildSymbolDisplay, we could register a callback to track symbols. Do we want analogous functionality for symbols used in creating type nodes (callback would be at https://github.com/Microsoft/TypeScript/compare/master...aozgaa:createTypeNode?expand=1#diff-c3ed224e4daa84352f7f1abcd23e8ccaR2576). This would be useful for checking whether the resulting identifiers are accessible under theenclosingDeclarationargument.in visit question token in parameter #14642, we discussed adding a tokenVisitor so that token's don't need to be visited except by callers who want to see them. We are such a caller as we use the textChanges API, which requires that we synthesize clones of all children, including tokens.(added)createSignatureDeclarationandupdateSignatureDeclarationare wonky (https://github.com/Microsoft/TypeScript/compare/master...aozgaa:createTypeNode?expand=1#diff-9c914fcc05c72a72db62c0a023b15f3fR392)