Skip to content

Commit edc3ed3

Browse files
committed
Merge pull request #8159 from Microsoft/declFileFirstTypeArgumentIsGenericFunctionType
Fixes scenarios of generating declaration file when first type argument is generic function type
2 parents fe4058e + 685900c commit edc3ed3

12 files changed

+714
-5
lines changed

src/compiler/checker.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1990,7 +1990,7 @@ namespace ts {
19901990
}
19911991
if (pos < end) {
19921992
writePunctuation(writer, SyntaxKind.LessThanToken);
1993-
writeType(typeArguments[pos], TypeFormatFlags.None);
1993+
writeType(typeArguments[pos], TypeFormatFlags.InFirstTypeArgument);
19941994
pos++;
19951995
while (pos < end) {
19961996
writePunctuation(writer, SyntaxKind.CommaToken);
@@ -2143,6 +2143,19 @@ namespace ts {
21432143
}
21442144
}
21452145

2146+
function shouldAddParenthesisAroundFunctionType(callSignature: Signature, flags: TypeFormatFlags) {
2147+
if (flags & TypeFormatFlags.InElementType) {
2148+
return true;
2149+
}
2150+
else if (flags & TypeFormatFlags.InFirstTypeArgument) {
2151+
// Add parenthesis around function type for the first type argument to avoid ambiguity
2152+
const typeParameters = callSignature.target && (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature) ?
2153+
callSignature.target.typeParameters : callSignature.typeParameters;
2154+
return typeParameters && typeParameters.length !== 0;
2155+
}
2156+
return false;
2157+
}
2158+
21462159
function writeLiteralType(type: ObjectType, flags: TypeFormatFlags) {
21472160
const resolved = resolveStructuredTypeMembers(type);
21482161
if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) {
@@ -2153,11 +2166,12 @@ namespace ts {
21532166
}
21542167

21552168
if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) {
2156-
if (flags & TypeFormatFlags.InElementType) {
2169+
const parenthesizeSignature = shouldAddParenthesisAroundFunctionType(resolved.callSignatures[0], flags);
2170+
if (parenthesizeSignature) {
21572171
writePunctuation(writer, SyntaxKind.OpenParenToken);
21582172
}
21592173
buildSignatureDisplay(resolved.callSignatures[0], writer, enclosingDeclaration, globalFlagsToPass | TypeFormatFlags.WriteArrowStyleSignature, /*kind*/ undefined, symbolStack);
2160-
if (flags & TypeFormatFlags.InElementType) {
2174+
if (parenthesizeSignature) {
21612175
writePunctuation(writer, SyntaxKind.CloseParenToken);
21622176
}
21632177
return;
@@ -2317,12 +2331,14 @@ namespace ts {
23172331
function buildDisplayForTypeArgumentsAndDelimiters(typeParameters: TypeParameter[], mapper: TypeMapper, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
23182332
if (typeParameters && typeParameters.length) {
23192333
writePunctuation(writer, SyntaxKind.LessThanToken);
2334+
let flags = TypeFormatFlags.InFirstTypeArgument;
23202335
for (let i = 0; i < typeParameters.length; i++) {
23212336
if (i > 0) {
23222337
writePunctuation(writer, SyntaxKind.CommaToken);
23232338
writeSpace(writer);
2339+
flags = TypeFormatFlags.None;
23242340
}
2325-
buildTypeDisplay(mapper(typeParameters[i]), writer, enclosingDeclaration, TypeFormatFlags.None);
2341+
buildTypeDisplay(mapper(typeParameters[i]), writer, enclosingDeclaration, flags);
23262342
}
23272343
writePunctuation(writer, SyntaxKind.GreaterThanToken);
23282344
}

src/compiler/declarationEmitter.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,6 +1377,7 @@ namespace ts {
13771377
function emitSignatureDeclaration(node: SignatureDeclaration) {
13781378
const prevEnclosingDeclaration = enclosingDeclaration;
13791379
enclosingDeclaration = node;
1380+
let closeParenthesizedFunctionType = false;
13801381

13811382
if (node.kind === SyntaxKind.IndexSignature) {
13821383
// Index signature can have readonly modifier
@@ -1388,6 +1389,16 @@ namespace ts {
13881389
if (node.kind === SyntaxKind.ConstructSignature || node.kind === SyntaxKind.ConstructorType) {
13891390
write("new ");
13901391
}
1392+
else if (node.kind === SyntaxKind.FunctionType) {
1393+
const currentOutput = writer.getText();
1394+
// Do not generate incorrect type when function type with type parameters is type argument
1395+
// This could happen if user used space between two '<' making it error free
1396+
// e.g var x: A< <Tany>(a: Tany)=>Tany>;
1397+
if (node.typeParameters && currentOutput.charAt(currentOutput.length - 1) === "<") {
1398+
closeParenthesizedFunctionType = true;
1399+
write("(");
1400+
}
1401+
}
13911402
emitTypeParameters(node.typeParameters);
13921403
write("(");
13931404
}
@@ -1421,6 +1432,9 @@ namespace ts {
14211432
write(";");
14221433
writeLine();
14231434
}
1435+
else if (closeParenthesizedFunctionType) {
1436+
write(")");
1437+
}
14241438

14251439
function getReturnTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
14261440
let diagnosticMessage: DiagnosticMessage;

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1820,6 +1820,7 @@ namespace ts {
18201820
WriteTypeArgumentsOfSignature = 0x00000020, // Write the type arguments instead of type parameters of the signature
18211821
InElementType = 0x00000040, // Writing an array or union element type
18221822
UseFullyQualifiedType = 0x00000080, // Write out the fully qualified type name (eg. Module.Type, instead of Type)
1823+
InFirstTypeArgument = 0x00000100, // Writing first type argument of the instantiated type
18231824
}
18241825

18251826
export const enum SymbolFormatFlags {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//// [declarationEmitFirstTypeArgumentGenericFunctionType.ts]
2+
3+
class X<A> {
4+
}
5+
var prop11: X< <Tany>() => Tany >; // spaces before the first type argument
6+
var prop12: X<(<Tany>() => Tany)>; // spaces before the first type argument
7+
function f1() { // Inferred return type
8+
return prop11;
9+
}
10+
function f2() { // Inferred return type
11+
return prop12;
12+
}
13+
function f3(): X< <Tany>() => Tany> { // written with space before type argument
14+
return prop11;
15+
}
16+
function f4(): X<(<Tany>() => Tany)> { // written type with parenthesis
17+
return prop12;
18+
}
19+
class Y<A, B> {
20+
}
21+
var prop2: Y<string[], <Tany>() => Tany>; // No space after second type argument
22+
var prop2: Y<string[], <Tany>() => Tany>; // space after second type argument
23+
var prop3: Y< <Tany>() => Tany, <Tany>() => Tany>; // space before first type argument
24+
var prop4: Y<(<Tany>() => Tany), <Tany>() => Tany>; // parenthesized first type argument
25+
26+
27+
//// [declarationEmitFirstTypeArgumentGenericFunctionType.js]
28+
class X {
29+
}
30+
var prop11; // spaces before the first type argument
31+
var prop12; // spaces before the first type argument
32+
function f1() {
33+
return prop11;
34+
}
35+
function f2() {
36+
return prop12;
37+
}
38+
function f3() {
39+
return prop11;
40+
}
41+
function f4() {
42+
return prop12;
43+
}
44+
class Y {
45+
}
46+
var prop2; // No space after second type argument
47+
var prop2; // space after second type argument
48+
var prop3; // space before first type argument
49+
var prop4; // parenthesized first type argument
50+
51+
52+
//// [declarationEmitFirstTypeArgumentGenericFunctionType.d.ts]
53+
declare class X<A> {
54+
}
55+
declare var prop11: X<(<Tany>() => Tany)>;
56+
declare var prop12: X<(<Tany>() => Tany)>;
57+
declare function f1(): X<(<Tany>() => Tany)>;
58+
declare function f2(): X<(<Tany>() => Tany)>;
59+
declare function f3(): X<(<Tany>() => Tany)>;
60+
declare function f4(): X<(<Tany>() => Tany)>;
61+
declare class Y<A, B> {
62+
}
63+
declare var prop2: Y<string[], <Tany>() => Tany>;
64+
declare var prop2: Y<string[], <Tany>() => Tany>;
65+
declare var prop3: Y<(<Tany>() => Tany), <Tany>() => Tany>;
66+
declare var prop4: Y<(<Tany>() => Tany), <Tany>() => Tany>;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
=== tests/cases/compiler/declarationEmitFirstTypeArgumentGenericFunctionType.ts ===
2+
3+
class X<A> {
4+
>X : Symbol(X, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 0, 0))
5+
>A : Symbol(A, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 1, 8))
6+
}
7+
var prop11: X< <Tany>() => Tany >; // spaces before the first type argument
8+
>prop11 : Symbol(prop11, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 3, 3))
9+
>X : Symbol(X, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 0, 0))
10+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 3, 16))
11+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 3, 16))
12+
13+
var prop12: X<(<Tany>() => Tany)>; // spaces before the first type argument
14+
>prop12 : Symbol(prop12, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 4, 3))
15+
>X : Symbol(X, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 0, 0))
16+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 4, 16))
17+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 4, 16))
18+
19+
function f1() { // Inferred return type
20+
>f1 : Symbol(f1, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 4, 34))
21+
22+
return prop11;
23+
>prop11 : Symbol(prop11, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 3, 3))
24+
}
25+
function f2() { // Inferred return type
26+
>f2 : Symbol(f2, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 7, 1))
27+
28+
return prop12;
29+
>prop12 : Symbol(prop12, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 4, 3))
30+
}
31+
function f3(): X< <Tany>() => Tany> { // written with space before type argument
32+
>f3 : Symbol(f3, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 10, 1))
33+
>X : Symbol(X, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 0, 0))
34+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 11, 19))
35+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 11, 19))
36+
37+
return prop11;
38+
>prop11 : Symbol(prop11, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 3, 3))
39+
}
40+
function f4(): X<(<Tany>() => Tany)> { // written type with parenthesis
41+
>f4 : Symbol(f4, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 13, 1))
42+
>X : Symbol(X, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 0, 0))
43+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 14, 19))
44+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 14, 19))
45+
46+
return prop12;
47+
>prop12 : Symbol(prop12, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 4, 3))
48+
}
49+
class Y<A, B> {
50+
>Y : Symbol(Y, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 16, 1))
51+
>A : Symbol(A, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 17, 8))
52+
>B : Symbol(B, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 17, 10))
53+
}
54+
var prop2: Y<string[], <Tany>() => Tany>; // No space after second type argument
55+
>prop2 : Symbol(prop2, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 19, 3), Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 20, 3))
56+
>Y : Symbol(Y, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 16, 1))
57+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 19, 24))
58+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 19, 24))
59+
60+
var prop2: Y<string[], <Tany>() => Tany>; // space after second type argument
61+
>prop2 : Symbol(prop2, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 19, 3), Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 20, 3))
62+
>Y : Symbol(Y, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 16, 1))
63+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 20, 24))
64+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 20, 24))
65+
66+
var prop3: Y< <Tany>() => Tany, <Tany>() => Tany>; // space before first type argument
67+
>prop3 : Symbol(prop3, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 21, 3))
68+
>Y : Symbol(Y, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 16, 1))
69+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 21, 15))
70+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 21, 15))
71+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 21, 33))
72+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 21, 33))
73+
74+
var prop4: Y<(<Tany>() => Tany), <Tany>() => Tany>; // parenthesized first type argument
75+
>prop4 : Symbol(prop4, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 22, 3))
76+
>Y : Symbol(Y, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 16, 1))
77+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 22, 15))
78+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 22, 15))
79+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 22, 34))
80+
>Tany : Symbol(Tany, Decl(declarationEmitFirstTypeArgumentGenericFunctionType.ts, 22, 34))
81+
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
=== tests/cases/compiler/declarationEmitFirstTypeArgumentGenericFunctionType.ts ===
2+
3+
class X<A> {
4+
>X : X<A>
5+
>A : A
6+
}
7+
var prop11: X< <Tany>() => Tany >; // spaces before the first type argument
8+
>prop11 : X<(<Tany>() => Tany)>
9+
>X : X<A>
10+
>Tany : Tany
11+
>Tany : Tany
12+
13+
var prop12: X<(<Tany>() => Tany)>; // spaces before the first type argument
14+
>prop12 : X<(<Tany>() => Tany)>
15+
>X : X<A>
16+
>Tany : Tany
17+
>Tany : Tany
18+
19+
function f1() { // Inferred return type
20+
>f1 : () => X<(<Tany>() => Tany)>
21+
22+
return prop11;
23+
>prop11 : X<(<Tany>() => Tany)>
24+
}
25+
function f2() { // Inferred return type
26+
>f2 : () => X<(<Tany>() => Tany)>
27+
28+
return prop12;
29+
>prop12 : X<(<Tany>() => Tany)>
30+
}
31+
function f3(): X< <Tany>() => Tany> { // written with space before type argument
32+
>f3 : () => X<(<Tany>() => Tany)>
33+
>X : X<A>
34+
>Tany : Tany
35+
>Tany : Tany
36+
37+
return prop11;
38+
>prop11 : X<(<Tany>() => Tany)>
39+
}
40+
function f4(): X<(<Tany>() => Tany)> { // written type with parenthesis
41+
>f4 : () => X<(<Tany>() => Tany)>
42+
>X : X<A>
43+
>Tany : Tany
44+
>Tany : Tany
45+
46+
return prop12;
47+
>prop12 : X<(<Tany>() => Tany)>
48+
}
49+
class Y<A, B> {
50+
>Y : Y<A, B>
51+
>A : A
52+
>B : B
53+
}
54+
var prop2: Y<string[], <Tany>() => Tany>; // No space after second type argument
55+
>prop2 : Y<string[], <Tany>() => Tany>
56+
>Y : Y<A, B>
57+
>Tany : Tany
58+
>Tany : Tany
59+
60+
var prop2: Y<string[], <Tany>() => Tany>; // space after second type argument
61+
>prop2 : Y<string[], <Tany>() => Tany>
62+
>Y : Y<A, B>
63+
>Tany : Tany
64+
>Tany : Tany
65+
66+
var prop3: Y< <Tany>() => Tany, <Tany>() => Tany>; // space before first type argument
67+
>prop3 : Y<(<Tany>() => Tany), <Tany>() => Tany>
68+
>Y : Y<A, B>
69+
>Tany : Tany
70+
>Tany : Tany
71+
>Tany : Tany
72+
>Tany : Tany
73+
74+
var prop4: Y<(<Tany>() => Tany), <Tany>() => Tany>; // parenthesized first type argument
75+
>prop4 : Y<(<Tany>() => Tany), <Tany>() => Tany>
76+
>Y : Y<A, B>
77+
>Tany : Tany
78+
>Tany : Tany
79+
>Tany : Tany
80+
>Tany : Tany
81+
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//// [declarationEmitPromise.ts]
2+
3+
export class bluebird<T> {
4+
static all: Array<bluebird<any>>;
5+
}
6+
7+
export async function runSampleWorks<A, B, C, D, E>(
8+
a: bluebird<A>, b?: bluebird<B>, c?: bluebird<C>, d?: bluebird<D>, e?: bluebird<E>) {
9+
let result = await (bluebird.all as any)([a, b, c, d, e].filter(el => !!el));
10+
let func = <T>(f: (a: A, b?: B, c?: C, d?: D, e?: E) => T): T =>
11+
f.apply(this, result);
12+
let rfunc: typeof func & {} = func as any; // <- This is the only difference
13+
return rfunc
14+
}
15+
16+
export async function runSampleBreaks<A, B, C, D, E>(
17+
a: bluebird<A>, b?: bluebird<B>, c?: bluebird<C>, d?: bluebird<D>, e?: bluebird<E>) {
18+
let result = await (bluebird.all as any)([a, b, c, d, e].filter(el => !!el));
19+
let func = <T>(f: (a: A, b?: B, c?: C, d?: D, e?: E) => T): T =>
20+
f.apply(this, result);
21+
let rfunc: typeof func = func as any; // <- This is the only difference
22+
return rfunc
23+
}
24+
25+
//// [declarationEmitPromise.js]
26+
"use strict";
27+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
28+
return new (P || (P = Promise))(function (resolve, reject) {
29+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
30+
function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } }
31+
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
32+
step((generator = generator.apply(thisArg, _arguments)).next());
33+
});
34+
};
35+
class bluebird {
36+
}
37+
exports.bluebird = bluebird;
38+
function runSampleWorks(a, b, c, d, e) {
39+
return __awaiter(this, void 0, void 0, function* () {
40+
let result = yield bluebird.all([a, b, c, d, e].filter(el => !!el));
41+
let func = (f) => f.apply(this, result);
42+
let rfunc = func; // <- This is the only difference
43+
return rfunc;
44+
});
45+
}
46+
exports.runSampleWorks = runSampleWorks;
47+
function runSampleBreaks(a, b, c, d, e) {
48+
return __awaiter(this, void 0, void 0, function* () {
49+
let result = yield bluebird.all([a, b, c, d, e].filter(el => !!el));
50+
let func = (f) => f.apply(this, result);
51+
let rfunc = func; // <- This is the only difference
52+
return rfunc;
53+
});
54+
}
55+
exports.runSampleBreaks = runSampleBreaks;
56+
57+
58+
//// [declarationEmitPromise.d.ts]
59+
export declare class bluebird<T> {
60+
static all: Array<bluebird<any>>;
61+
}
62+
export declare function runSampleWorks<A, B, C, D, E>(a: bluebird<A>, b?: bluebird<B>, c?: bluebird<C>, d?: bluebird<D>, e?: bluebird<E>): Promise<(<T>(f: (a: A, b?: B, c?: C, d?: D, e?: E) => T) => T) & {}>;
63+
export declare function runSampleBreaks<A, B, C, D, E>(a: bluebird<A>, b?: bluebird<B>, c?: bluebird<C>, d?: bluebird<D>, e?: bluebird<E>): Promise<(<T>(f: (a: A, b?: B, c?: C, d?: D, e?: E) => T) => T)>;

0 commit comments

Comments
 (0)