Type to string via type node#15790
Conversation
* pass context as argument in xToNode methods * make sourcefile optional in printer * start consolidating NodeBuilderFlags and TypeFormatFlags
| /*@internal*/ autoGenerateKind?: GeneratedIdentifierKind; // Specifies whether to auto-generate the text for an identifier. | ||
| /*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name. | ||
| isInJSDocNamespace?: boolean; // if the node is a member in a JSDoc namespace | ||
| /*@internal*/ typeArguments?: NodeArray<TypeNode>; // Only defined on synthesized nodes.Though not syntactically valid, used in emitting diagnostics. |
There was a problem hiding this comment.
nit: spacing for comments.
| SuppressAnyReturnType = 1 << 8, // If the return type is any-like, don't offer a return type. | ||
|
|
||
| // Error handling | ||
| allowThisInObjectLiteral = 1 << 10, |
There was a problem hiding this comment.
Mixed case for first letter. Prefer one or the other.
| export const enum NewLineKind { | ||
| CarriageReturnLineFeed = 0, | ||
| LineFeed = 1, | ||
| None = 2 |
There was a problem hiding this comment.
Prefer we use EmitFlags.SingleLine instead of adding this member.
| } | ||
|
|
||
| export const enum EmitFlags { | ||
| None = 0, |
| * collisions. | ||
| */ | ||
| printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string; | ||
| printNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined): string; |
There was a problem hiding this comment.
Don't allow undefined for source file on public API.
|
|
||
| case SyntaxKind.PropertySignature: | ||
| return updatePropertySignature((<PropertySignature>node), | ||
| nodesVisitor((<PropertySignature>node).modifiers, visitor, isToken), |
There was a problem hiding this comment.
Add this to reduceEachChild as well.
| } | ||
|
|
||
| function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind): IndexSignatureDeclaration { | ||
| function mapToTypeNodeArray(types: Type[], context: NodeBuilderContext, addInElementTypeFlag: boolean, addInFirstTypeArgumentFlag: boolean): TypeNode[] { |
There was a problem hiding this comment.
Consider adding parenthesization logic to the factory methods for types, similar to what we do for expressions. This would ensure we always handle parentheses consistently and would simplify much of the logic here.
| _incrementExpressionBrand: any; | ||
| _unaryExpressionBrand: any; | ||
| _expressionBrand: any; | ||
| typeArguments: any; |
There was a problem hiding this comment.
Give this the right type, but mark it /*@internal*/
| var x = "\u{abcd}\u{ef12}\u{3456}\u{7890}"; | ||
| >x : string | ||
| >"\u{abcd}\u{ef12}\u{3456}\u{7890}" : "ꯍ㑖碐" | ||
| >"\u{abcd}\u{ef12}\u{3456}\u{7890}" : "\uABCD\uEF12\u3456\u7890" |
There was a problem hiding this comment.
We should probably fix these cases.
There was a problem hiding this comment.
Consider adding support for textSourceNode from StringLiteral to Identifier so that you can just pull the raw text from the original source file. You would have to modify both getTextOfNode and getLiteralTextOfNode in emitter.ts to make a best effort to find the source file of the node if currentSourceFile is undefined, and otherwise just use the node's text.
| //// class C implements I {[| |]} | ||
|
|
||
| verify.rangeAfterCodeFix(` | ||
| test(a: [string, number]): void { |
There was a problem hiding this comment.
Why can't we use the alias name?
There was a problem hiding this comment.
Sometimes we really ought to instantiate the type reference. Consider:
interface I<T> {
foo(t: T): void;
}
class C implements I<number> {}Then we clearly ought to use the instantiated type for a.
It looks like it will be tricky to distinguish the above case and other variants from the test case.
The behavior in the test doesn't represent a regression.
Because the fix is complicated, I'll leave it for a separate PR.
| IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine, | ||
| ObjectBindingPatternElements = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings, | ||
| ObjectBindingPatternElements = SingleLine | CommaDelimited | SpaceBetweenSiblings, | ||
| ObjectBindingPatternElementsWithSpaceBetweenBraces = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings, |
There was a problem hiding this comment.
Consider reverting this in a separate PR, and simplify the names (especially for the general emit case).
rbuckton
left a comment
There was a problem hiding this comment.
Approved with a few minor comments.
|
|
||
| export function parenthesizeElementTypeMembers(members: TypeNode[]) { | ||
| // TODO: does this lose `originalNode` ptr? | ||
| return createNodeArray(members.map(parenthesizeElementTypeMember)); |
There was a problem hiding this comment.
Consider using sameMap. Also do you need to resolve the TODO here?
| } | ||
|
|
||
| export function parenthesizeTypeParameters(typeParameters: TypeNode[]) { | ||
| if (typeParameters && typeParameters.length > 0) { |
| export function parenthesizeTypeParameters(typeParameters: TypeNode[]) { | ||
| if (typeParameters && typeParameters.length > 0) { | ||
| const nodeArray = createNodeArray(typeParameters); | ||
| const firstEntry = nodeArray[0]; |
There was a problem hiding this comment.
Create a fresh node array, createNodeArray actually just turns typeParameters into a node array, so you end up mutating an existing array.
| return false; | ||
| } | ||
|
|
||
| export function isFunctionOrConstructor(node: Node): node is FunctionTypeNode | ConstructorTypeNode { |
There was a problem hiding this comment.
Maybe isFunctionOrConstructorTypeNode?
| /*@internal*/ typeArguments?: NodeArray<TypeNode>; // Only defined on synthesized nodes.Though not syntactically valid, used in emitting diagnostics. | ||
| /*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name. | ||
| isInJSDocNamespace?: boolean; // if the node is a member in a JSDoc namespace | ||
| /*@internal*/ typeArguments?: NodeArray<TypeNode>; // Only defined on synthesized nodes.Though not syntactically valid, used in emitting diagnostics. |
There was a problem hiding this comment.
nit: spacing nodes.Though
| const inFirstTypeArgument = context.flags & NodeBuilderFlags.InFirstTypeArgument; | ||
| const inTypeAlias = context.flags & NodeBuilderFlags.InTypeAlias; | ||
| context.flags &= ~(NodeBuilderFlags.StateClearingFlags); | ||
| context.flags &= ~(NodeBuilderFlags.InTypeAlias); |
| isSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { | ||
| const name = symbolToTypeReferenceName(type.aliasSymbol); | ||
| const typeArgumentNodes = toTypeArgumentNodes(type.aliasTypeArguments, context); | ||
| const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments, context); |
There was a problem hiding this comment.
consider moving existential check into mapToTypeNodeArray
| else if (type.target.objectFlags & ObjectFlags.Tuple) { | ||
| if (typeArguments.length > 0) { | ||
| const tupleConstituentNodes = mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type)), context, /*addInElementTypeFlag*/ false, /*addInFirstTypeArgumentFlag*/ false); | ||
| const slice = typeArguments.slice(0, getTypeReferenceArity(type)); |
There was a problem hiding this comment.
If you add the check to mapToTypeNodeArray you don't need to break this up into two statements.
| // the default outer type arguments), we don't show the group. | ||
| if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { | ||
| const typeArgumentNodes = createNodeArray(toTypeArgumentNodes(typeArguments.slice(start, i), context)); | ||
| const slice = typeArguments.slice(start, i); |
| const typeParameterCount = (type.target.typeParameters || emptyArray).length; | ||
| const slice = typeArguments && typeArguments.slice(i, typeParameterCount); | ||
| typeArgumentNodes = toTypeArgumentNodes(slice, context); | ||
| typeArgumentNodes = slice && mapToTypeNodeArray(slice, context); |
Fixes #14444, and a number of miscellaneous issues with
TypeNodecreation, including:and moves us closer to consolidating printing infrastructure. Will still need to make an emitter building display parts to eliminate
SymbolDisplayBuildermethods.