Skip to content

Commit 384d546

Browse files
author
Arthur Ozga
committed
Basic parens handling and stubbed TypeFormatFlags
1 parent d0f3716 commit 384d546

File tree

6 files changed

+107
-25
lines changed

6 files changed

+107
-25
lines changed

src/compiler/checker.ts

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2287,8 +2287,16 @@ namespace ts {
22872287
interface NodeBuilderContext {
22882288
readonly enclosingDeclaration: Node | undefined;
22892289
readonly flags: NodeBuilderFlags | undefined;
2290+
2291+
// State
22902292
encounteredError: boolean;
22912293
inObjectTypeLiteral: boolean;
2294+
// TODO: needed for part of parens handling
2295+
InElementType: boolean; // Writing an array or union element type
2296+
// TODO: ???
2297+
InFirstTypeArgument: boolean; // Writing first type argument of the instantiated type
2298+
// TODO: ???
2299+
InTypeAlias: boolean; // Writing type in type alias declaration
22922300
checkAlias: boolean;
22932301
symbolStack: Symbol[] | undefined;
22942302
}
@@ -2299,18 +2307,28 @@ namespace ts {
22992307
flags,
23002308
encounteredError: false,
23012309
inObjectTypeLiteral: false,
2310+
InElementType: false,
2311+
InFirstTypeArgument: false,
2312+
InTypeAlias: false,
23022313
checkAlias: true,
23032314
symbolStack: undefined
23042315
};
23052316
}
23062317

23072318
function typeToTypeNodeHelper(type: Type, context: NodeBuilderContext): TypeNode {
2319+
const InElementType = context.InElementType;
2320+
// TODO: why doesn't tts unset the flag?
2321+
context.InElementType = false;
2322+
2323+
// TODO: should be assert?
23082324
if (!type) {
23092325
context.encounteredError = true;
23102326
// TODO(aozgaa): should we return implict any (undefined) or explicit any (keywordtypenode)?
23112327
return undefined;
23122328
}
23132329

2330+
2331+
23142332
if (type.flags & TypeFlags.Any) {
23152333
return createKeywordTypeNode(SyntaxKind.AnyKeyword);
23162334
}
@@ -2390,16 +2408,17 @@ namespace ts {
23902408

23912409
if (context.checkAlias && type.aliasSymbol) {
23922410
const name = symbolToName(type.aliasSymbol, /*expectsIdentifier*/ false, context);
2393-
const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments);
2411+
const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments, /*addInElementTypeFlag*/ false);
23942412
return createTypeReferenceNode(name, typeArgumentNodes);
23952413
}
23962414
context.checkAlias = false;
23972415

2398-
if (type.flags & TypeFlags.Union) {
2399-
const formattedUnionTypes = formatUnionTypes((<UnionType>type).types);
2400-
const unionTypeNodes = formattedUnionTypes && mapToTypeNodeArray(formattedUnionTypes);
2401-
if (unionTypeNodes && unionTypeNodes.length > 0) {
2402-
return createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, unionTypeNodes);
2416+
if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) {
2417+
const types = type.flags & TypeFlags.Union ? formatUnionTypes((<UnionType>type).types) : (<IntersectionType>type).types;
2418+
const typeNodes = types && mapToTypeNodeArray(types, /*addInElementTypeFlag*/ true);
2419+
if (typeNodes && typeNodes.length > 0) {
2420+
const unionOrIntersectionTypeNode = createUnionOrIntersectionTypeNode(type.flags & TypeFlags.Union ? SyntaxKind.UnionType : SyntaxKind.IntersectionType, typeNodes);
2421+
return InElementType ? createParenthesizedTypeNode(unionOrIntersectionTypeNode) : unionOrIntersectionTypeNode;
24032422
}
24042423
else {
24052424
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowEmptyUnionOrIntersection)) {
@@ -2409,10 +2428,6 @@ namespace ts {
24092428
}
24102429
}
24112430

2412-
if (type.flags & TypeFlags.Intersection) {
2413-
return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, mapToTypeNodeArray((type as UnionType).types));
2414-
}
2415-
24162431
if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) {
24172432
Debug.assert(!!(type.flags & TypeFlags.Object));
24182433
// The type is an object literal type.
@@ -2421,25 +2436,33 @@ namespace ts {
24212436

24222437
if (type.flags & TypeFlags.Index) {
24232438
const indexedType = (<IndexType>type).type;
2439+
context.InElementType = <boolean>true;
24242440
const indexTypeNode = typeToTypeNodeHelper(indexedType, context);
2441+
Debug.assert(context.InElementType === false);
24252442
return createTypeOperatorNode(indexTypeNode);
24262443
}
2444+
24272445
if (type.flags & TypeFlags.IndexedAccess) {
2446+
context.InElementType = <boolean>true;
24282447
const objectTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).objectType, context);
2448+
Debug.assert(context.InElementType === false);
24292449
const indexTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).indexType, context);
24302450
return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode);
24312451
}
24322452

24332453
Debug.fail("Should be unreachable.");
24342454

2435-
function mapToTypeNodeArray(types: Type[]): TypeNode[] {
2455+
function mapToTypeNodeArray(types: Type[], addInElementTypeFlag: boolean): TypeNode[] {
24362456
const result = [];
2457+
Debug.assert(context.InElementType === false, "should be unset at the beginning of the helper");
24372458
for (const type of types) {
2459+
context.InElementType = addInElementTypeFlag;
24382460
const typeNode = typeToTypeNodeHelper(type, context);
24392461
if (typeNode) {
24402462
result.push(typeNode);
24412463
}
24422464
}
2465+
Debug.assert(context.InElementType === false, "should be unset at the beginning of the helper");
24432466
return result;
24442467
}
24452468

@@ -2523,6 +2546,7 @@ namespace ts {
25232546

25242547
if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) {
25252548
const signature = resolved.callSignatures[0];
2549+
shouldAddParenthesisAroundFunctionType(signature, context);
25262550
return <FunctionTypeNode>signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, context);
25272551
}
25282552
if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) {
@@ -2538,6 +2562,20 @@ namespace ts {
25382562
return createTypeLiteralNode(members);
25392563
}
25402564

2565+
2566+
function shouldAddParenthesisAroundFunctionType(callSignature: Signature, context: NodeBuilderContext) {
2567+
if (context.InElementType) {
2568+
return true;
2569+
}
2570+
else if (context.InFirstTypeArgument) {
2571+
// Add parenthesis around function type for the first type argument to avoid ambiguity
2572+
const typeParameters = callSignature.target && (context.flags & NodeBuilderFlags.WriteTypeArgumentsOfSignature) ?
2573+
callSignature.target.typeParameters : callSignature.typeParameters;
2574+
return typeParameters && typeParameters.length !== 0;
2575+
}
2576+
return false;
2577+
}
2578+
25412579
function createTypeQueryNodeFromSymbol(symbol: Symbol) {
25422580
const entityName = symbolToName(symbol, /*expectsIdentifier*/ false, context);
25432581
return createTypeQueryNode(entityName);
@@ -2546,12 +2584,14 @@ namespace ts {
25462584
function typeReferenceToTypeNode(type: TypeReference) {
25472585
const typeArguments: Type[] = type.typeArguments || emptyArray;
25482586
if (type.target === globalArrayType) {
2587+
context.InElementType = true;
25492588
const elementType = typeToTypeNodeHelper(typeArguments[0], context);
2589+
context.InElementType = false;
25502590
return createArrayTypeNode(elementType);
25512591
}
25522592
else if (type.target.objectFlags & ObjectFlags.Tuple) {
25532593
if (typeArguments.length > 0) {
2554-
const tupleConstituentNodes = mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type)));
2594+
const tupleConstituentNodes = mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type)), /*addInElementTypeFlag*/ false);
25552595
if (tupleConstituentNodes && tupleConstituentNodes.length > 0) {
25562596
return createTupleTypeNode(tupleConstituentNodes);
25572597
}
@@ -2566,6 +2606,7 @@ namespace ts {
25662606
let i = 0;
25672607
let qualifiedName: QualifiedName | undefined = undefined;
25682608
if (outerTypeParameters) {
2609+
let inFirstTypeArgument = true;
25692610
const length = outerTypeParameters.length;
25702611
while (i < length) {
25712612
// Find group of type arguments for type parameters with the same declaring container.
@@ -2577,6 +2618,7 @@ namespace ts {
25772618
// When type parameters are their own type arguments for the whole group (i.e. we have
25782619
// the default outer type arguments), we don't show the group.
25792620
if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) {
2621+
// inFirstTypeArgument???
25802622
const qualifiedNamePart = symbolToName(parent, /*expectsIdentifier*/ true, context);
25812623
if (!qualifiedName) {
25822624
qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/ undefined);
@@ -2587,6 +2629,7 @@ namespace ts {
25872629
qualifiedName = createQualifiedName(qualifiedName, /*right*/ undefined);
25882630
}
25892631
}
2632+
inFirstTypeArgument = false;
25902633
}
25912634
}
25922635
let entityName: EntityName = undefined;
@@ -2600,7 +2643,7 @@ namespace ts {
26002643
entityName = nameIdentifier;
26012644
}
26022645
const typeParameterCount = (type.target.typeParameters || emptyArray).length;
2603-
const typeArgumentNodes = some(typeArguments) ? mapToTypeNodeArray(typeArguments.slice(i, typeParameterCount - i)) : undefined;
2646+
const typeArgumentNodes = some(typeArguments) ? mapToTypeNodeArray(typeArguments.slice(i, typeParameterCount - i), /*addInElementTypeFlag*/ false) : undefined;
26042647
return createTypeReferenceNode(entityName, typeArgumentNodes);
26052648
}
26062649
}
@@ -2695,7 +2738,7 @@ namespace ts {
26952738
const returnType = getReturnTypeOfSignature(signature);
26962739
returnTypeNode = returnType && typeToTypeNodeHelper(returnType, context);
26972740
}
2698-
if(context.flags & NodeBuilderFlags.suppressAnyReturnType) {
2741+
if(context.flags & NodeBuilderFlags.SuppressAnyReturnType) {
26992742
if(returnTypeNode && returnTypeNode.kind === SyntaxKind.AnyKeyword) {
27002743
returnTypeNode = undefined;
27012744
}
@@ -2733,6 +2776,8 @@ namespace ts {
27332776
return parameterNode;
27342777
}
27352778

2779+
// TODO: add meaning: SymbolFlags argument.
2780+
// TODO: add SymbolFormatFlags?? Yes to add outer type parameters. Defer UseOnlyExternalAliasing until a separate symbolbuilder PR.
27362781
function symbolToName(symbol: Symbol, expectsIdentifier: true, context: NodeBuilderContext): Identifier;
27372782
function symbolToName(symbol: Symbol, expectsIdentifier: false, context: NodeBuilderContext): EntityName;
27382783
function symbolToName(symbol: Symbol, expectsIdentifier: boolean, context: NodeBuilderContext): EntityName {
@@ -2826,6 +2871,7 @@ namespace ts {
28262871
}
28272872
}
28282873
}
2874+
28292875
function getNameOfSymbol(symbol: Symbol, context: NodeBuilderContext): string {
28302876
const declaration = firstOrUndefined(symbol.declarations);
28312877
if (declaration) {

src/compiler/emitter.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2947,8 +2947,9 @@ namespace ts {
29472947
// Precomputed Formats
29482948
Modifiers = SingleLine | SpaceBetweenSiblings,
29492949
HeritageClauses = SingleLine | SpaceBetweenSiblings,
2950-
SingleLineTypeLiteralMembers = SpaceBetweenBraces | SpaceBetweenSiblings | Indented, // MultiLine | Indented,
2950+
SingleLineTypeLiteralMembers = SingleLine | SpaceBetweenBraces | SpaceBetweenSiblings | Indented, // MultiLine | Indented,
29512951
MultiLineTypeLiteralMembers = MultiLine | Indented,
2952+
29522953
TupleTypeElements = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented,
29532954
UnionTypeConstituents = BarDelimited | SpaceBetweenSiblings | SingleLine,
29542955
IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine,

src/compiler/factory.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,18 @@ namespace ts {
393393
: node;
394394
}
395395

396+
export function createParenthesizedTypeNode(type: TypeNode) {
397+
const parenthesizedTypeNode = createSynthesizedNode(SyntaxKind.ParenthesizedType) as ParenthesizedTypeNode;
398+
parenthesizedTypeNode.type = type;
399+
return parenthesizedTypeNode;
400+
}
401+
402+
export function updateParenthesizedTypeNode(node: ParenthesizedTypeNode, type: TypeNode) {
403+
return node.type !== type
404+
? updateNode(createParenthesizedTypeNode(type), node)
405+
: node;
406+
}
407+
396408
export function createTypeLiteralNode(members: TypeElement[]) {
397409
const typeLiteralNode = createSynthesizedNode(SyntaxKind.TypeLiteral) as TypeLiteralNode;
398410
typeLiteralNode.members = createNodeArray(members);

src/compiler/types.ts

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2559,14 +2559,36 @@ namespace ts {
25592559

25602560
export enum NodeBuilderFlags {
25612561
None = 0,
2562-
allowThisInObjectLiteral = 1 << 0,
2563-
allowQualifedNameInPlaceOfIdentifier = 1 << 1,
2564-
allowTypeParameterInQualifiedName = 1 << 2,
2565-
allowAnonymousIdentifier = 1 << 3,
2566-
allowEmptyUnionOrIntersection = 1 << 4,
2567-
allowEmptyTuple = 1 << 5,
2568-
suppressAnyReturnType = 1 << 6,
2569-
ignoreErrors = allowThisInObjectLiteral | allowQualifedNameInPlaceOfIdentifier | allowTypeParameterInQualifiedName | allowAnonymousIdentifier | allowEmptyUnionOrIntersection | allowEmptyTuple
2562+
// Options
2563+
NoTruncation = 1 << 0, // Don't truncate result
2564+
// TODO: part of emit.
2565+
WriteArrayAsGenericType = 1 << 1, // Write Array<T> instead T[]
2566+
// TODO: part of emit.
2567+
UseTypeOfFunction = 1 << 2, // Write typeof instead of function type literal
2568+
// TODO: part of emit.
2569+
WriteArrowStyleSignature = 1 << 3, // Write arrow style signature
2570+
// TODO: turn it into a failing type reference?
2571+
WriteOwnNameForAnyLike = 1 << 4, // Write symbol's own name instead of 'any' for any like types (eg. unknown, __resolving__ etc)
2572+
// TODO
2573+
WriteTypeArgumentsOfSignature = 1 << 5, // Write the type arguments instead of type parameters of the signature
2574+
// TODO
2575+
UseFullyQualifiedType = 1 << 6, // Write out the fully qualified type name (eg. Module.Type, instead of Type)
2576+
// TODO
2577+
UseTypeAliasValue = 1 << 7, // Serialize the type instead of using type-alias. This is needed when we emit declaration file.
2578+
SuppressAnyReturnType = 1 << 8, // If the return type is any-like, don't offer a return type.
2579+
// TODO
2580+
AddUndefined = 1 << 9, // Add undefined to types of initialized, non-optional parameters
2581+
2582+
// Error handling
2583+
allowThisInObjectLiteral = 1 << 10,
2584+
allowQualifedNameInPlaceOfIdentifier = 1 << 11,
2585+
allowTypeParameterInQualifiedName = 1 << 12,
2586+
allowAnonymousIdentifier = 1 << 13,
2587+
allowEmptyUnionOrIntersection = 1 << 14,
2588+
allowEmptyTuple = 1 << 15,
2589+
2590+
ignoreErrors = allowThisInObjectLiteral | allowQualifedNameInPlaceOfIdentifier | allowTypeParameterInQualifiedName | allowAnonymousIdentifier | allowEmptyUnionOrIntersection | allowEmptyTuple,
2591+
25702592
}
25712593

25722594
export interface SymbolDisplayBuilder {

src/compiler/visitor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,8 @@ namespace ts {
322322
nodesVisitor((<UnionOrIntersectionTypeNode>node).types, visitor, isTypeNode));
323323

324324
case SyntaxKind.ParenthesizedType:
325-
throw Debug.fail("not implemented.");
325+
return updateParenthesizedTypeNode(<ParenthesizedTypeNode>node,
326+
visitNode((<ParenthesizedTypeNode>node).type, visitor, isTypeNode));
326327

327328
case SyntaxKind.TypeOperator:
328329
return updateTypeOperatorNode(<TypeOperatorNode>node, visitNode((<TypeOperatorNode>node).type, visitor, isTypeNode));

src/services/codefixes/helpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ namespace ts.codefix {
130130
}
131131

132132
function signatureToMethodDeclaration(signature: Signature, enclosingDeclaration: Node, body?: Block) {
133-
const signatureDeclaration = <MethodDeclaration>checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration, NodeBuilderFlags.suppressAnyReturnType);
133+
const signatureDeclaration = <MethodDeclaration>checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration, NodeBuilderFlags.SuppressAnyReturnType);
134134
if (signatureDeclaration) {
135135
signatureDeclaration.decorators = undefined;
136136
signatureDeclaration.modifiers = modifiers;

0 commit comments

Comments
 (0)