Skip to content

Commit 482accc

Browse files
committed
Union this-types of unioned call signatures
And and tests and baselines
1 parent e7aa7e4 commit 482accc

5 files changed

Lines changed: 116 additions & 8 deletions

File tree

src/compiler/checker.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3610,9 +3610,9 @@ namespace ts {
36103610
setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexInfo, arrayType.numberIndexInfo);
36113611
}
36123612

3613-
function findMatchingSignature(signatureList: Signature[], signature: Signature, partialMatch: boolean, ignoreReturnTypes: boolean): Signature {
3613+
function findMatchingSignature(signatureList: Signature[], signature: Signature, partialMatch: boolean, ignoreThisTypes: boolean, ignoreReturnTypes: boolean): Signature {
36143614
for (const s of signatureList) {
3615-
if (compareSignaturesIdentical(s, signature, partialMatch, ignoreReturnTypes, compareTypesIdentical)) {
3615+
if (compareSignaturesIdentical(s, signature, partialMatch, ignoreThisTypes, ignoreReturnTypes, compareTypesIdentical)) {
36163616
return s;
36173617
}
36183618
}
@@ -3626,7 +3626,7 @@ namespace ts {
36263626
return undefined;
36273627
}
36283628
for (let i = 1; i < signatureLists.length; i++) {
3629-
if (!findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ false, /*ignoreReturnTypes*/ false)) {
3629+
if (!findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ false)) {
36303630
return undefined;
36313631
}
36323632
}
@@ -3635,7 +3635,7 @@ namespace ts {
36353635
let result: Signature[] = undefined;
36363636
for (let i = 0; i < signatureLists.length; i++) {
36373637
// Allow matching non-generic signatures to have excess parameters and different return types
3638-
const match = i === listIndex ? signature : findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ true, /*ignoreReturnTypes*/ true);
3638+
const match = i === listIndex ? signature : findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ true, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true);
36393639
if (!match) {
36403640
return undefined;
36413641
}
@@ -3656,13 +3656,16 @@ namespace ts {
36563656
for (let i = 0; i < signatureLists.length; i++) {
36573657
for (const signature of signatureLists[i]) {
36583658
// Only process signatures with parameter lists that aren't already in the result list
3659-
if (!result || !findMatchingSignature(result, signature, /*partialMatch*/ false, /*ignoreReturnTypes*/ true)) {
3659+
if (!result || !findMatchingSignature(result, signature, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true)) {
36603660
const unionSignatures = findMatchingSignatures(signatureLists, signature, i);
36613661
if (unionSignatures) {
36623662
let s = signature;
36633663
// Union the result types when more than one signature matches
36643664
if (unionSignatures.length > 1) {
36653665
s = cloneSignature(signature);
3666+
if (forEach(unionSignatures, sig => sig.thisType)) {
3667+
s.thisType = getUnionType(map(unionSignatures, sig => sig.thisType || anyType));
3668+
}
36663669
// Clear resolved return type we possibly got from cloneSignature
36673670
s.resolvedReturnType = undefined;
36683671
s.unionSignatures = unionSignatures;
@@ -6002,7 +6005,7 @@ namespace ts {
60026005
}
60036006
let result = Ternary.True;
60046007
for (let i = 0, len = sourceSignatures.length; i < len; i++) {
6005-
const related = compareSignaturesIdentical(sourceSignatures[i], targetSignatures[i], /*partialMatch*/ false, /*ignoreReturnTypes*/ false, isRelatedTo);
6008+
const related = compareSignaturesIdentical(sourceSignatures[i], targetSignatures[i], /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ false, isRelatedTo);
60066009
if (!related) {
60076010
return Ternary.False;
60086011
}
@@ -6203,7 +6206,7 @@ namespace ts {
62036206
/**
62046207
* See signatureRelatedTo, compareSignaturesIdentical
62056208
*/
6206-
function compareSignaturesIdentical(source: Signature, target: Signature, partialMatch: boolean, ignoreReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary {
6209+
function compareSignaturesIdentical(source: Signature, target: Signature, partialMatch: boolean, ignoreThisTypes: boolean, ignoreReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary {
62076210
// TODO (drosen): De-duplicate code between related functions.
62086211
if (source === target) {
62096212
return Ternary.True;
@@ -6224,6 +6227,13 @@ namespace ts {
62246227
source = getErasedSignature(source);
62256228
target = getErasedSignature(target);
62266229
let result = Ternary.True;
6230+
if (!ignoreThisTypes && source.thisType && target.thisType) {
6231+
const related = compareTypes(source.thisType, target.thisType);
6232+
if (!related) {
6233+
return Ternary.False;
6234+
}
6235+
result &= related;
6236+
}
62276237
const targetLen = target.parameters.length;
62286238
for (let i = 0; i < targetLen; i++) {
62296239
const s = isRestParameterIndex(source, i) ? getRestTypeOfSignature(source) : getTypeOfSymbol(source.parameters[i]);
@@ -8137,7 +8147,7 @@ namespace ts {
81378147
// This signature will contribute to contextual union signature
81388148
signatureList = [signature];
81398149
}
8140-
else if (!compareSignaturesIdentical(signatureList[0], signature, /*partialMatch*/ false, /*ignoreReturnTypes*/ true, compareTypesIdentical)) {
8150+
else if (!compareSignaturesIdentical(signatureList[0], signature, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true, compareTypesIdentical)) {
81418151
// Signatures aren't identical, do not use
81428152
return undefined;
81438153
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//// [unionThisTypeInFunctions.ts]
2+
interface Real {
3+
method(n: number): void;
4+
data: string;
5+
}
6+
interface Fake {
7+
method(n: number): void;
8+
data: number;
9+
}
10+
function test(r: Real | Fake) {
11+
r.method(12);
12+
}
13+
14+
15+
//// [unionThisTypeInFunctions.js]
16+
function test(r) {
17+
r.method(12);
18+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
=== tests/cases/conformance/types/thisType/unionThisTypeInFunctions.ts ===
2+
interface Real {
3+
>Real : Symbol(Real, Decl(unionThisTypeInFunctions.ts, 0, 0))
4+
5+
method(n: number): void;
6+
>method : Symbol(method, Decl(unionThisTypeInFunctions.ts, 0, 16))
7+
>n : Symbol(n, Decl(unionThisTypeInFunctions.ts, 1, 11))
8+
9+
data: string;
10+
>data : Symbol(data, Decl(unionThisTypeInFunctions.ts, 1, 28))
11+
}
12+
interface Fake {
13+
>Fake : Symbol(Fake, Decl(unionThisTypeInFunctions.ts, 3, 1))
14+
15+
method(n: number): void;
16+
>method : Symbol(method, Decl(unionThisTypeInFunctions.ts, 4, 16))
17+
>n : Symbol(n, Decl(unionThisTypeInFunctions.ts, 5, 11))
18+
19+
data: number;
20+
>data : Symbol(data, Decl(unionThisTypeInFunctions.ts, 5, 28))
21+
}
22+
function test(r: Real | Fake) {
23+
>test : Symbol(test, Decl(unionThisTypeInFunctions.ts, 7, 1))
24+
>r : Symbol(r, Decl(unionThisTypeInFunctions.ts, 8, 14))
25+
>Real : Symbol(Real, Decl(unionThisTypeInFunctions.ts, 0, 0))
26+
>Fake : Symbol(Fake, Decl(unionThisTypeInFunctions.ts, 3, 1))
27+
28+
r.method(12);
29+
>r.method : Symbol(method, Decl(unionThisTypeInFunctions.ts, 0, 16), Decl(unionThisTypeInFunctions.ts, 4, 16))
30+
>r : Symbol(r, Decl(unionThisTypeInFunctions.ts, 8, 14))
31+
>method : Symbol(method, Decl(unionThisTypeInFunctions.ts, 0, 16), Decl(unionThisTypeInFunctions.ts, 4, 16))
32+
}
33+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
=== tests/cases/conformance/types/thisType/unionThisTypeInFunctions.ts ===
2+
interface Real {
3+
>Real : Real
4+
5+
method(n: number): void;
6+
>method : (this: this, n: number) => void
7+
>n : number
8+
9+
data: string;
10+
>data : string
11+
}
12+
interface Fake {
13+
>Fake : Fake
14+
15+
method(n: number): void;
16+
>method : (this: this, n: number) => void
17+
>n : number
18+
19+
data: number;
20+
>data : number
21+
}
22+
function test(r: Real | Fake) {
23+
>test : (this: void, r: Real | Fake) => void
24+
>r : Real | Fake
25+
>Real : Real
26+
>Fake : Fake
27+
28+
r.method(12);
29+
>r.method(12) : void
30+
>r.method : ((this: Real, n: number) => void) | ((this: Fake, n: number) => void)
31+
>r : Real | Fake
32+
>method : ((this: Real, n: number) => void) | ((this: Fake, n: number) => void)
33+
>12 : number
34+
}
35+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// @strictThis: true
2+
interface Real {
3+
method(n: number): void;
4+
data: string;
5+
}
6+
interface Fake {
7+
method(n: number): void;
8+
data: number;
9+
}
10+
function test(r: Real | Fake) {
11+
r.method(12);
12+
}

0 commit comments

Comments
 (0)