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
This commit is contained in:
Jihn Dai 2022-02-15 18:21:44 -04:00 committed by GitHub
parent 67172e41c2
commit 1e60c8702c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 277 additions and 2 deletions

View File

@ -13077,8 +13077,11 @@ namespace ts {
// object type literal or interface (using the new keyword). Each way of declaring a constructor
// will result in a different declaration kind.
if (!signature.isolatedSignatureType) {
const kind = signature.declaration ? signature.declaration.kind : SyntaxKind.Unknown;
const isConstructor = kind === SyntaxKind.Constructor || kind === SyntaxKind.ConstructSignature || kind === SyntaxKind.ConstructorType;
const kind = signature.declaration?.kind;
// If declaration is undefined, it is likely to be the signature of the default constructor.
const isConstructor = kind === undefined || kind === SyntaxKind.Constructor || kind === SyntaxKind.ConstructSignature || kind === SyntaxKind.ConstructorType;
const type = createObjectType(ObjectFlags.Anonymous);
type.members = emptySymbols;
type.properties = emptyArray;

View File

@ -0,0 +1,66 @@
//// [inferringReturnTypeFromConstructSignatureGeneric.ts]
class GenericObject<T extends {} = {}> {
give(value: T) {
return value;
}
}
class GenericNumber<T extends number> {
give(value: T) {
return value;
}
}
class GenericNumberOrString<T extends number | string> {
give(value: T) {
return value;
}
}
function g<T>(type: new () => T): T {
return new type();
}
const g1 = g(GenericObject);
g1.give({});
const g2 = g(GenericNumber);
g2.give(1);
const g3 = g(GenericNumberOrString);
g3.give(1);
g3.give('1');
//// [inferringReturnTypeFromConstructSignatureGeneric.js]
var GenericObject = /** @class */ (function () {
function GenericObject() {
}
GenericObject.prototype.give = function (value) {
return value;
};
return GenericObject;
}());
var GenericNumber = /** @class */ (function () {
function GenericNumber() {
}
GenericNumber.prototype.give = function (value) {
return value;
};
return GenericNumber;
}());
var GenericNumberOrString = /** @class */ (function () {
function GenericNumberOrString() {
}
GenericNumberOrString.prototype.give = function (value) {
return value;
};
return GenericNumberOrString;
}());
function g(type) {
return new type();
}
var g1 = g(GenericObject);
g1.give({});
var g2 = g(GenericNumber);
g2.give(1);
var g3 = g(GenericNumberOrString);
g3.give(1);
g3.give('1');

View File

@ -0,0 +1,87 @@
=== tests/cases/compiler/inferringReturnTypeFromConstructSignatureGeneric.ts ===
class GenericObject<T extends {} = {}> {
>GenericObject : Symbol(GenericObject, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 0, 0))
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 0, 20))
give(value: T) {
>give : Symbol(GenericObject.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 0, 40))
>value : Symbol(value, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 1, 7))
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 0, 20))
return value;
>value : Symbol(value, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 1, 7))
}
}
class GenericNumber<T extends number> {
>GenericNumber : Symbol(GenericNumber, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 4, 1))
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 5, 20))
give(value: T) {
>give : Symbol(GenericNumber.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 5, 39))
>value : Symbol(value, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 6, 7))
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 5, 20))
return value;
>value : Symbol(value, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 6, 7))
}
}
class GenericNumberOrString<T extends number | string> {
>GenericNumberOrString : Symbol(GenericNumberOrString, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 9, 1))
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 10, 28))
give(value: T) {
>give : Symbol(GenericNumberOrString.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 10, 56))
>value : Symbol(value, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 11, 7))
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 10, 28))
return value;
>value : Symbol(value, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 11, 7))
}
}
function g<T>(type: new () => T): T {
>g : Symbol(g, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 14, 1))
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 16, 11))
>type : Symbol(type, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 16, 14))
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 16, 11))
>T : Symbol(T, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 16, 11))
return new type();
>type : Symbol(type, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 16, 14))
}
const g1 = g(GenericObject);
>g1 : Symbol(g1, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 20, 5))
>g : Symbol(g, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 14, 1))
>GenericObject : Symbol(GenericObject, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 0, 0))
g1.give({});
>g1.give : Symbol(GenericObject.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 0, 40))
>g1 : Symbol(g1, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 20, 5))
>give : Symbol(GenericObject.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 0, 40))
const g2 = g(GenericNumber);
>g2 : Symbol(g2, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 23, 5))
>g : Symbol(g, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 14, 1))
>GenericNumber : Symbol(GenericNumber, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 4, 1))
g2.give(1);
>g2.give : Symbol(GenericNumber.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 5, 39))
>g2 : Symbol(g2, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 23, 5))
>give : Symbol(GenericNumber.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 5, 39))
const g3 = g(GenericNumberOrString);
>g3 : Symbol(g3, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 26, 5))
>g : Symbol(g, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 14, 1))
>GenericNumberOrString : Symbol(GenericNumberOrString, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 9, 1))
g3.give(1);
>g3.give : Symbol(GenericNumberOrString.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 10, 56))
>g3 : Symbol(g3, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 26, 5))
>give : Symbol(GenericNumberOrString.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 10, 56))
g3.give('1');
>g3.give : Symbol(GenericNumberOrString.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 10, 56))
>g3 : Symbol(g3, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 26, 5))
>give : Symbol(GenericNumberOrString.give, Decl(inferringReturnTypeFromConstructSignatureGeneric.ts, 10, 56))

View File

@ -0,0 +1,90 @@
=== tests/cases/compiler/inferringReturnTypeFromConstructSignatureGeneric.ts ===
class GenericObject<T extends {} = {}> {
>GenericObject : GenericObject<T>
give(value: T) {
>give : (value: T) => T
>value : T
return value;
>value : T
}
}
class GenericNumber<T extends number> {
>GenericNumber : GenericNumber<T>
give(value: T) {
>give : (value: T) => T
>value : T
return value;
>value : T
}
}
class GenericNumberOrString<T extends number | string> {
>GenericNumberOrString : GenericNumberOrString<T>
give(value: T) {
>give : (value: T) => T
>value : T
return value;
>value : T
}
}
function g<T>(type: new () => T): T {
>g : <T>(type: new () => T) => T
>type : new () => T
return new type();
>new type() : T
>type : new () => T
}
const g1 = g(GenericObject);
>g1 : GenericObject<{}>
>g(GenericObject) : GenericObject<{}>
>g : <T>(type: new () => T) => T
>GenericObject : typeof GenericObject
g1.give({});
>g1.give({}) : {}
>g1.give : (value: {}) => {}
>g1 : GenericObject<{}>
>give : (value: {}) => {}
>{} : {}
const g2 = g(GenericNumber);
>g2 : GenericNumber<number>
>g(GenericNumber) : GenericNumber<number>
>g : <T>(type: new () => T) => T
>GenericNumber : typeof GenericNumber
g2.give(1);
>g2.give(1) : number
>g2.give : (value: number) => number
>g2 : GenericNumber<number>
>give : (value: number) => number
>1 : 1
const g3 = g(GenericNumberOrString);
>g3 : GenericNumberOrString<string | number>
>g(GenericNumberOrString) : GenericNumberOrString<string | number>
>g : <T>(type: new () => T) => T
>GenericNumberOrString : typeof GenericNumberOrString
g3.give(1);
>g3.give(1) : string | number
>g3.give : (value: string | number) => string | number
>g3 : GenericNumberOrString<string | number>
>give : (value: string | number) => string | number
>1 : 1
g3.give('1');
>g3.give('1') : string | number
>g3.give : (value: string | number) => string | number
>g3 : GenericNumberOrString<string | number>
>give : (value: string | number) => string | number
>'1' : "1"

View File

@ -0,0 +1,29 @@
class GenericObject<T extends {} = {}> {
give(value: T) {
return value;
}
}
class GenericNumber<T extends number> {
give(value: T) {
return value;
}
}
class GenericNumberOrString<T extends number | string> {
give(value: T) {
return value;
}
}
function g<T>(type: new () => T): T {
return new type();
}
const g1 = g(GenericObject);
g1.give({});
const g2 = g(GenericNumber);
g2.give(1);
const g3 = g(GenericNumberOrString);
g3.give(1);
g3.give('1');