diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e989c8c8a04..9d101aab909 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1990,7 +1990,7 @@ namespace ts { } if (pos < end) { writePunctuation(writer, SyntaxKind.LessThanToken); - writeType(typeArguments[pos], TypeFormatFlags.None); + writeType(typeArguments[pos], TypeFormatFlags.InFirstTypeArgument); pos++; while (pos < end) { writePunctuation(writer, SyntaxKind.CommaToken); @@ -2143,6 +2143,19 @@ namespace ts { } } + function shouldAddParenthesisAroundFunctionType(callSignature: Signature, flags: TypeFormatFlags) { + if (flags & TypeFormatFlags.InElementType) { + return true; + } + else if (flags & TypeFormatFlags.InFirstTypeArgument) { + // Add parenthesis around function type for the first type argument to avoid ambiguity + const typeParameters = callSignature.target && (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature) ? + callSignature.target.typeParameters : callSignature.typeParameters; + return typeParameters && typeParameters.length !== 0; + } + return false; + } + function writeLiteralType(type: ObjectType, flags: TypeFormatFlags) { const resolved = resolveStructuredTypeMembers(type); if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) { @@ -2153,11 +2166,12 @@ namespace ts { } if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { - if (flags & TypeFormatFlags.InElementType) { + const parenthesizeSignature = shouldAddParenthesisAroundFunctionType(resolved.callSignatures[0], flags); + if (parenthesizeSignature) { writePunctuation(writer, SyntaxKind.OpenParenToken); } buildSignatureDisplay(resolved.callSignatures[0], writer, enclosingDeclaration, globalFlagsToPass | TypeFormatFlags.WriteArrowStyleSignature, /*kind*/ undefined, symbolStack); - if (flags & TypeFormatFlags.InElementType) { + if (parenthesizeSignature) { writePunctuation(writer, SyntaxKind.CloseParenToken); } return; @@ -2317,12 +2331,14 @@ namespace ts { function buildDisplayForTypeArgumentsAndDelimiters(typeParameters: TypeParameter[], mapper: TypeMapper, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { if (typeParameters && typeParameters.length) { writePunctuation(writer, SyntaxKind.LessThanToken); + let flags = TypeFormatFlags.InFirstTypeArgument; for (let i = 0; i < typeParameters.length; i++) { if (i > 0) { writePunctuation(writer, SyntaxKind.CommaToken); writeSpace(writer); + flags = TypeFormatFlags.None; } - buildTypeDisplay(mapper(typeParameters[i]), writer, enclosingDeclaration, TypeFormatFlags.None); + buildTypeDisplay(mapper(typeParameters[i]), writer, enclosingDeclaration, flags); } writePunctuation(writer, SyntaxKind.GreaterThanToken); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 718be453b09..c39a4b10cbf 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1820,6 +1820,7 @@ namespace ts { WriteTypeArgumentsOfSignature = 0x00000020, // Write the type arguments instead of type parameters of the signature InElementType = 0x00000040, // Writing an array or union element type UseFullyQualifiedType = 0x00000080, // Write out the fully qualified type name (eg. Module.Type, instead of Type) + InFirstTypeArgument = 0x00000100, // Writing first type argument of the instantiated type } export const enum SymbolFormatFlags { diff --git a/tests/baselines/reference/declarationEmitPromise.js b/tests/baselines/reference/declarationEmitPromise.js index 7387623dfe2..a4bbdd74dde 100644 --- a/tests/baselines/reference/declarationEmitPromise.js +++ b/tests/baselines/reference/declarationEmitPromise.js @@ -60,63 +60,4 @@ export declare class bluebird { static all: Array>; } export declare function runSampleWorks(a: bluebird, b?: bluebird, c?: bluebird, d?: bluebird, e?: bluebird): Promise<((f: (a: A, b?: B, c?: C, d?: D, e?: E) => T) => T) & {}>; -export declare function runSampleBreaks(a: bluebird, b?: bluebird, c?: bluebird, d?: bluebird, e?: bluebird): Promise<(f: (a: A, b?: B, c?: C, d?: D, e?: E) => T) => T>; - - -//// [DtsFileErrors] - - -tests/cases/compiler/declarationEmitPromise.d.ts(5,141): error TS2314: Generic type 'Promise' requires 1 type argument(s). -tests/cases/compiler/declarationEmitPromise.d.ts(5,148): error TS1144: '{' or ';' expected. -tests/cases/compiler/declarationEmitPromise.d.ts(5,150): error TS2304: Cannot find name 'T'. -tests/cases/compiler/declarationEmitPromise.d.ts(5,153): error TS2304: Cannot find name 'f'. -tests/cases/compiler/declarationEmitPromise.d.ts(5,154): error TS1005: ')' expected. -tests/cases/compiler/declarationEmitPromise.d.ts(5,160): error TS2304: Cannot find name 'A'. -tests/cases/compiler/declarationEmitPromise.d.ts(5,167): error TS2304: Cannot find name 'B'. -tests/cases/compiler/declarationEmitPromise.d.ts(5,174): error TS2304: Cannot find name 'C'. -tests/cases/compiler/declarationEmitPromise.d.ts(5,181): error TS2304: Cannot find name 'D'. -tests/cases/compiler/declarationEmitPromise.d.ts(5,188): error TS2304: Cannot find name 'E'. -tests/cases/compiler/declarationEmitPromise.d.ts(5,194): error TS2304: Cannot find name 'T'. -tests/cases/compiler/declarationEmitPromise.d.ts(5,195): error TS1005: ';' expected. -tests/cases/compiler/declarationEmitPromise.d.ts(5,197): error TS1128: Declaration or statement expected. -tests/cases/compiler/declarationEmitPromise.d.ts(5,200): error TS2304: Cannot find name 'T'. -tests/cases/compiler/declarationEmitPromise.d.ts(5,202): error TS1109: Expression expected. - - -==== tests/cases/compiler/declarationEmitPromise.d.ts (15 errors) ==== - export declare class bluebird { - static all: Array>; - } - export declare function runSampleWorks(a: bluebird, b?: bluebird, c?: bluebird, d?: bluebird, e?: bluebird): Promise<((f: (a: A, b?: B, c?: C, d?: D, e?: E) => T) => T) & {}>; - export declare function runSampleBreaks(a: bluebird, b?: bluebird, c?: bluebird, d?: bluebird, e?: bluebird): Promise<(f: (a: A, b?: B, c?: C, d?: D, e?: E) => T) => T>; - ~~~~~~~ -!!! error TS2314: Generic type 'Promise' requires 1 type argument(s). - ~~ -!!! error TS1144: '{' or ';' expected. - ~ -!!! error TS2304: Cannot find name 'T'. - ~ -!!! error TS2304: Cannot find name 'f'. - ~ -!!! error TS1005: ')' expected. - ~ -!!! error TS2304: Cannot find name 'A'. - ~ -!!! error TS2304: Cannot find name 'B'. - ~ -!!! error TS2304: Cannot find name 'C'. - ~ -!!! error TS2304: Cannot find name 'D'. - ~ -!!! error TS2304: Cannot find name 'E'. - ~ -!!! error TS2304: Cannot find name 'T'. - ~ -!!! error TS1005: ';' expected. - ~~ -!!! error TS1128: Declaration or statement expected. - ~ -!!! error TS2304: Cannot find name 'T'. - ~ -!!! error TS1109: Expression expected. - \ No newline at end of file +export declare function runSampleBreaks(a: bluebird, b?: bluebird, c?: bluebird, d?: bluebird, e?: bluebird): Promise<((f: (a: A, b?: B, c?: C, d?: D, e?: E) => T) => T)>; diff --git a/tests/baselines/reference/declarationEmitPromise.types b/tests/baselines/reference/declarationEmitPromise.types index c423d5c68f7..0306c72762b 100644 --- a/tests/baselines/reference/declarationEmitPromise.types +++ b/tests/baselines/reference/declarationEmitPromise.types @@ -96,7 +96,7 @@ export async function runSampleWorks( } export async function runSampleBreaks( ->runSampleBreaks : (a: bluebird, b?: bluebird, c?: bluebird, d?: bluebird, e?: bluebird) => Promise<(f: (a: A, b?: B, c?: C, d?: D, e?: E) => T) => T> +>runSampleBreaks : (a: bluebird, b?: bluebird, c?: bluebird, d?: bluebird, e?: bluebird) => Promise<((f: (a: A, b?: B, c?: C, d?: D, e?: E) => T) => T)> >A : A >B : B >C : C diff --git a/tests/baselines/reference/specializedLambdaTypeArguments.types b/tests/baselines/reference/specializedLambdaTypeArguments.types index 3eb2fbc959e..b10da421044 100644 --- a/tests/baselines/reference/specializedLambdaTypeArguments.types +++ b/tests/baselines/reference/specializedLambdaTypeArguments.types @@ -4,7 +4,7 @@ class X { >A : A prop: X< () => Tany >; ->prop : X<() => Tany> +>prop : X<(() => Tany)> >X : X >Tany : Tany >Tany : Tany