@@ -2287,8 +2287,16 @@ namespace ts {
2287
2287
interface NodeBuilderContext {
2288
2288
readonly enclosingDeclaration: Node | undefined;
2289
2289
readonly flags: NodeBuilderFlags | undefined;
2290
+
2291
+ // State
2290
2292
encounteredError: boolean;
2291
2293
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
2292
2300
checkAlias: boolean;
2293
2301
symbolStack: Symbol[] | undefined;
2294
2302
}
@@ -2299,18 +2307,28 @@ namespace ts {
2299
2307
flags,
2300
2308
encounteredError: false,
2301
2309
inObjectTypeLiteral: false,
2310
+ InElementType: false,
2311
+ InFirstTypeArgument: false,
2312
+ InTypeAlias: false,
2302
2313
checkAlias: true,
2303
2314
symbolStack: undefined
2304
2315
};
2305
2316
}
2306
2317
2307
2318
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?
2308
2324
if (!type) {
2309
2325
context.encounteredError = true;
2310
2326
// TODO(aozgaa): should we return implict any (undefined) or explicit any (keywordtypenode)?
2311
2327
return undefined;
2312
2328
}
2313
2329
2330
+
2331
+
2314
2332
if (type.flags & TypeFlags.Any) {
2315
2333
return createKeywordTypeNode(SyntaxKind.AnyKeyword);
2316
2334
}
@@ -2390,16 +2408,17 @@ namespace ts {
2390
2408
2391
2409
if (context.checkAlias && type.aliasSymbol) {
2392
2410
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 );
2394
2412
return createTypeReferenceNode(name, typeArgumentNodes);
2395
2413
}
2396
2414
context.checkAlias = false;
2397
2415
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;
2403
2422
}
2404
2423
else {
2405
2424
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowEmptyUnionOrIntersection)) {
@@ -2409,10 +2428,6 @@ namespace ts {
2409
2428
}
2410
2429
}
2411
2430
2412
- if (type.flags & TypeFlags.Intersection) {
2413
- return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, mapToTypeNodeArray((type as UnionType).types));
2414
- }
2415
-
2416
2431
if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) {
2417
2432
Debug.assert(!!(type.flags & TypeFlags.Object));
2418
2433
// The type is an object literal type.
@@ -2421,25 +2436,33 @@ namespace ts {
2421
2436
2422
2437
if (type.flags & TypeFlags.Index) {
2423
2438
const indexedType = (<IndexType>type).type;
2439
+ context.InElementType = <boolean>true;
2424
2440
const indexTypeNode = typeToTypeNodeHelper(indexedType, context);
2441
+ Debug.assert(context.InElementType === false);
2425
2442
return createTypeOperatorNode(indexTypeNode);
2426
2443
}
2444
+
2427
2445
if (type.flags & TypeFlags.IndexedAccess) {
2446
+ context.InElementType = <boolean>true;
2428
2447
const objectTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).objectType, context);
2448
+ Debug.assert(context.InElementType === false);
2429
2449
const indexTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).indexType, context);
2430
2450
return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode);
2431
2451
}
2432
2452
2433
2453
Debug.fail("Should be unreachable.");
2434
2454
2435
- function mapToTypeNodeArray(types: Type[]): TypeNode[] {
2455
+ function mapToTypeNodeArray(types: Type[], addInElementTypeFlag: boolean ): TypeNode[] {
2436
2456
const result = [];
2457
+ Debug.assert(context.InElementType === false, "should be unset at the beginning of the helper");
2437
2458
for (const type of types) {
2459
+ context.InElementType = addInElementTypeFlag;
2438
2460
const typeNode = typeToTypeNodeHelper(type, context);
2439
2461
if (typeNode) {
2440
2462
result.push(typeNode);
2441
2463
}
2442
2464
}
2465
+ Debug.assert(context.InElementType === false, "should be unset at the beginning of the helper");
2443
2466
return result;
2444
2467
}
2445
2468
@@ -2523,6 +2546,7 @@ namespace ts {
2523
2546
2524
2547
if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) {
2525
2548
const signature = resolved.callSignatures[0];
2549
+ shouldAddParenthesisAroundFunctionType(signature, context);
2526
2550
return <FunctionTypeNode>signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, context);
2527
2551
}
2528
2552
if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) {
@@ -2538,6 +2562,20 @@ namespace ts {
2538
2562
return createTypeLiteralNode(members);
2539
2563
}
2540
2564
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
+
2541
2579
function createTypeQueryNodeFromSymbol(symbol: Symbol) {
2542
2580
const entityName = symbolToName(symbol, /*expectsIdentifier*/ false, context);
2543
2581
return createTypeQueryNode(entityName);
@@ -2546,12 +2584,14 @@ namespace ts {
2546
2584
function typeReferenceToTypeNode(type: TypeReference) {
2547
2585
const typeArguments: Type[] = type.typeArguments || emptyArray;
2548
2586
if (type.target === globalArrayType) {
2587
+ context.InElementType = true;
2549
2588
const elementType = typeToTypeNodeHelper(typeArguments[0], context);
2589
+ context.InElementType = false;
2550
2590
return createArrayTypeNode(elementType);
2551
2591
}
2552
2592
else if (type.target.objectFlags & ObjectFlags.Tuple) {
2553
2593
if (typeArguments.length > 0) {
2554
- const tupleConstituentNodes = mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type)));
2594
+ const tupleConstituentNodes = mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type)), /*addInElementTypeFlag*/ false );
2555
2595
if (tupleConstituentNodes && tupleConstituentNodes.length > 0) {
2556
2596
return createTupleTypeNode(tupleConstituentNodes);
2557
2597
}
@@ -2566,6 +2606,7 @@ namespace ts {
2566
2606
let i = 0;
2567
2607
let qualifiedName: QualifiedName | undefined = undefined;
2568
2608
if (outerTypeParameters) {
2609
+ let inFirstTypeArgument = true;
2569
2610
const length = outerTypeParameters.length;
2570
2611
while (i < length) {
2571
2612
// Find group of type arguments for type parameters with the same declaring container.
@@ -2577,6 +2618,7 @@ namespace ts {
2577
2618
// When type parameters are their own type arguments for the whole group (i.e. we have
2578
2619
// the default outer type arguments), we don't show the group.
2579
2620
if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) {
2621
+ // inFirstTypeArgument???
2580
2622
const qualifiedNamePart = symbolToName(parent, /*expectsIdentifier*/ true, context);
2581
2623
if (!qualifiedName) {
2582
2624
qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/ undefined);
@@ -2587,6 +2629,7 @@ namespace ts {
2587
2629
qualifiedName = createQualifiedName(qualifiedName, /*right*/ undefined);
2588
2630
}
2589
2631
}
2632
+ inFirstTypeArgument = false;
2590
2633
}
2591
2634
}
2592
2635
let entityName: EntityName = undefined;
@@ -2600,7 +2643,7 @@ namespace ts {
2600
2643
entityName = nameIdentifier;
2601
2644
}
2602
2645
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;
2604
2647
return createTypeReferenceNode(entityName, typeArgumentNodes);
2605
2648
}
2606
2649
}
@@ -2695,7 +2738,7 @@ namespace ts {
2695
2738
const returnType = getReturnTypeOfSignature(signature);
2696
2739
returnTypeNode = returnType && typeToTypeNodeHelper(returnType, context);
2697
2740
}
2698
- if(context.flags & NodeBuilderFlags.suppressAnyReturnType ) {
2741
+ if(context.flags & NodeBuilderFlags.SuppressAnyReturnType ) {
2699
2742
if(returnTypeNode && returnTypeNode.kind === SyntaxKind.AnyKeyword) {
2700
2743
returnTypeNode = undefined;
2701
2744
}
@@ -2733,6 +2776,8 @@ namespace ts {
2733
2776
return parameterNode;
2734
2777
}
2735
2778
2779
+ // TODO: add meaning: SymbolFlags argument.
2780
+ // TODO: add SymbolFormatFlags?? Yes to add outer type parameters. Defer UseOnlyExternalAliasing until a separate symbolbuilder PR.
2736
2781
function symbolToName(symbol: Symbol, expectsIdentifier: true, context: NodeBuilderContext): Identifier;
2737
2782
function symbolToName(symbol: Symbol, expectsIdentifier: false, context: NodeBuilderContext): EntityName;
2738
2783
function symbolToName(symbol: Symbol, expectsIdentifier: boolean, context: NodeBuilderContext): EntityName {
@@ -2826,6 +2871,7 @@ namespace ts {
2826
2871
}
2827
2872
}
2828
2873
}
2874
+
2829
2875
function getNameOfSymbol(symbol: Symbol, context: NodeBuilderContext): string {
2830
2876
const declaration = firstOrUndefined(symbol.declarations);
2831
2877
if (declaration) {
0 commit comments