@@ -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) {
0 commit comments