Skip to content

Commit 1e60c87

Browse files
authored
Fix inference for generic-typed constructor parameter when no explicit constructor is present (#47750)
* assume signature is from constructor if declaration is undefined * add tests and baselines
1 parent 67172e4 commit 1e60c87

5 files changed

+277
-2
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13077,8 +13077,11 @@ namespace ts {
1307713077
// object type literal or interface (using the new keyword). Each way of declaring a constructor
1307813078
// will result in a different declaration kind.
1307913079
if (!signature.isolatedSignatureType) {
13080-
const kind = signature.declaration ? signature.declaration.kind : SyntaxKind.Unknown;
13081-
const isConstructor = kind === SyntaxKind.Constructor || kind === SyntaxKind.ConstructSignature || kind === SyntaxKind.ConstructorType;
13080+
const kind = signature.declaration?.kind;
13081+
13082+
// If declaration is undefined, it is likely to be the signature of the default constructor.
13083+
const isConstructor = kind === undefined || kind === SyntaxKind.Constructor || kind === SyntaxKind.ConstructSignature || kind === SyntaxKind.ConstructorType;
13084+
1308213085
const type = createObjectType(ObjectFlags.Anonymous);
1308313086
type.members = emptySymbols;
1308413087
type.properties = emptyArray;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//// [inferringReturnTypeFromConstructSignatureGeneric.ts]
2+
class GenericObject<T extends {} = {}> {
3+
give(value: T) {
4+
return value;
5+
}
6+
}
7+
class GenericNumber<T extends number> {
8+
give(value: T) {
9+
return value;
10+
}
11+
}
12+
class GenericNumberOrString<T extends number | string> {
13+
give(value: T) {
14+
return value;
15+
}
16+
}
17+
18+
function g<T>(type: new () => T): T {
19+
return new type();
20+
}
21+
22+
const g1 = g(GenericObject);
23+
g1.give({});
24+
25+
const g2 = g(GenericNumber);
26+
g2.give(1);
27+
28+
const g3 = g(GenericNumberOrString);
29+
g3.give(1);
30+
g3.give('1');
31+
32+
//// [inferringReturnTypeFromConstructSignatureGeneric.js]
33+
var GenericObject = /** @class */ (function () {
34+
function GenericObject() {
35+
}
36+
GenericObject.prototype.give = function (value) {
37+
return value;
38+
};
39+
return GenericObject;
40+
}());
41+
var GenericNumber = /** @class */ (function () {
42+
function GenericNumber() {
43+
}
44+
GenericNumber.prototype.give = function (value) {
45+
return value;
46+
};
47+
return GenericNumber;
48+
}());
49+
var GenericNumberOrString = /** @class */ (function () {
50+
function GenericNumberOrString() {
51+
}
52+
GenericNumberOrString.prototype.give = function (value) {
53+
return value;
54+
};
55+
return GenericNumberOrString;
56+
}());
57+
function g(type) {
58+
return new type();
59+
}
60+
var g1 = g(GenericObject);
61+
g1.give({});
62+
var g2 = g(GenericNumber);
63+
g2.give(1);
64+
var g3 = g(GenericNumberOrString);
65+
g3.give(1);
66+
g3.give('1');
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
=== tests/cases/compiler/inferringReturnTypeFromConstructSignatureGeneric.ts ===
2+
class GenericObject<T extends {} = {}> {
3+
>GenericObject : Symbol(GenericObject, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 0, 0))
4+
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 0, 20))
5+
6+
give(value: T) {
7+
>give : Symbol(GenericObject.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 0, 40))
8+
>value : Symbol(value, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 1, 7))
9+
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 0, 20))
10+
11+
return value;
12+
>value : Symbol(value, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 1, 7))
13+
}
14+
}
15+
class GenericNumber<T extends number> {
16+
>GenericNumber : Symbol(GenericNumber, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 4, 1))
17+
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 5, 20))
18+
19+
give(value: T) {
20+
>give : Symbol(GenericNumber.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 5, 39))
21+
>value : Symbol(value, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 6, 7))
22+
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 5, 20))
23+
24+
return value;
25+
>value : Symbol(value, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 6, 7))
26+
}
27+
}
28+
class GenericNumberOrString<T extends number | string> {
29+
>GenericNumberOrString : Symbol(GenericNumberOrString, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 9, 1))
30+
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 10, 28))
31+
32+
give(value: T) {
33+
>give : Symbol(GenericNumberOrString.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 10, 56))
34+
>value : Symbol(value, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 11, 7))
35+
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 10, 28))
36+
37+
return value;
38+
>value : Symbol(value, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 11, 7))
39+
}
40+
}
41+
42+
function g<T>(type: new () => T): T {
43+
>g : Symbol(g, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 14, 1))
44+
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 16, 11))
45+
>type : Symbol(type, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 16, 14))
46+
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 16, 11))
47+
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 16, 11))
48+
49+
return new type();
50+
>type : Symbol(type, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 16, 14))
51+
}
52+
53+
const g1 = g(GenericObject);
54+
>g1 : Symbol(g1, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 20, 5))
55+
>g : Symbol(g, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 14, 1))
56+
>GenericObject : Symbol(GenericObject, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 0, 0))
57+
58+
g1.give({});
59+
>g1.give : Symbol(GenericObject.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 0, 40))
60+
>g1 : Symbol(g1, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 20, 5))
61+
>give : Symbol(GenericObject.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 0, 40))
62+
63+
const g2 = g(GenericNumber);
64+
>g2 : Symbol(g2, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 23, 5))
65+
>g : Symbol(g, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 14, 1))
66+
>GenericNumber : Symbol(GenericNumber, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 4, 1))
67+
68+
g2.give(1);
69+
>g2.give : Symbol(GenericNumber.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 5, 39))
70+
>g2 : Symbol(g2, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 23, 5))
71+
>give : Symbol(GenericNumber.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 5, 39))
72+
73+
const g3 = g(GenericNumberOrString);
74+
>g3 : Symbol(g3, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 26, 5))
75+
>g : Symbol(g, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 14, 1))
76+
>GenericNumberOrString : Symbol(GenericNumberOrString, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 9, 1))
77+
78+
g3.give(1);
79+
>g3.give : Symbol(GenericNumberOrString.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 10, 56))
80+
>g3 : Symbol(g3, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 26, 5))
81+
>give : Symbol(GenericNumberOrString.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 10, 56))
82+
83+
g3.give('1');
84+
>g3.give : Symbol(GenericNumberOrString.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 10, 56))
85+
>g3 : Symbol(g3, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 26, 5))
86+
>give : Symbol(GenericNumberOrString.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 10, 56))
87+
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
=== tests/cases/compiler/inferringReturnTypeFromConstructSignatureGeneric.ts ===
2+
class GenericObject<T extends {} = {}> {
3+
>GenericObject : GenericObject<T>
4+
5+
give(value: T) {
6+
>give : (value: T) => T
7+
>value : T
8+
9+
return value;
10+
>value : T
11+
}
12+
}
13+
class GenericNumber<T extends number> {
14+
>GenericNumber : GenericNumber<T>
15+
16+
give(value: T) {
17+
>give : (value: T) => T
18+
>value : T
19+
20+
return value;
21+
>value : T
22+
}
23+
}
24+
class GenericNumberOrString<T extends number | string> {
25+
>GenericNumberOrString : GenericNumberOrString<T>
26+
27+
give(value: T) {
28+
>give : (value: T) => T
29+
>value : T
30+
31+
return value;
32+
>value : T
33+
}
34+
}
35+
36+
function g<T>(type: new () => T): T {
37+
>g : <T>(type: new () => T) => T
38+
>type : new () => T
39+
40+
return new type();
41+
>new type() : T
42+
>type : new () => T
43+
}
44+
45+
const g1 = g(GenericObject);
46+
>g1 : GenericObject<{}>
47+
>g(GenericObject) : GenericObject<{}>
48+
>g : <T>(type: new () => T) => T
49+
>GenericObject : typeof GenericObject
50+
51+
g1.give({});
52+
>g1.give({}) : {}
53+
>g1.give : (value: {}) => {}
54+
>g1 : GenericObject<{}>
55+
>give : (value: {}) => {}
56+
>{} : {}
57+
58+
const g2 = g(GenericNumber);
59+
>g2 : GenericNumber<number>
60+
>g(GenericNumber) : GenericNumber<number>
61+
>g : <T>(type: new () => T) => T
62+
>GenericNumber : typeof GenericNumber
63+
64+
g2.give(1);
65+
>g2.give(1) : number
66+
>g2.give : (value: number) => number
67+
>g2 : GenericNumber<number>
68+
>give : (value: number) => number
69+
>1 : 1
70+
71+
const g3 = g(GenericNumberOrString);
72+
>g3 : GenericNumberOrString<string | number>
73+
>g(GenericNumberOrString) : GenericNumberOrString<string | number>
74+
>g : <T>(type: new () => T) => T
75+
>GenericNumberOrString : typeof GenericNumberOrString
76+
77+
g3.give(1);
78+
>g3.give(1) : string | number
79+
>g3.give : (value: string | number) => string | number
80+
>g3 : GenericNumberOrString<string | number>
81+
>give : (value: string | number) => string | number
82+
>1 : 1
83+
84+
g3.give('1');
85+
>g3.give('1') : string | number
86+
>g3.give : (value: string | number) => string | number
87+
>g3 : GenericNumberOrString<string | number>
88+
>give : (value: string | number) => string | number
89+
>'1' : "1"
90+
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
class GenericObject<T extends {} = {}> {
2+
give(value: T) {
3+
return value;
4+
}
5+
}
6+
class GenericNumber<T extends number> {
7+
give(value: T) {
8+
return value;
9+
}
10+
}
11+
class GenericNumberOrString<T extends number | string> {
12+
give(value: T) {
13+
return value;
14+
}
15+
}
16+
17+
function g<T>(type: new () => T): T {
18+
return new type();
19+
}
20+
21+
const g1 = g(GenericObject);
22+
g1.give({});
23+
24+
const g2 = g(GenericNumber);
25+
g2.give(1);
26+
27+
const g3 = g(GenericNumberOrString);
28+
g3.give(1);
29+
g3.give('1');

0 commit comments

Comments
 (0)