Union this-types of unioned call signatures

And and tests and baselines
This commit is contained in:
Nathan Shively-Sanders 2016-03-08 13:05:00 -08:00
parent e7aa7e4958
commit 482acccada
5 changed files with 116 additions and 8 deletions

View File

@ -3610,9 +3610,9 @@ namespace ts {
setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexInfo, arrayType.numberIndexInfo);
}
function findMatchingSignature(signatureList: Signature[], signature: Signature, partialMatch: boolean, ignoreReturnTypes: boolean): Signature {
function findMatchingSignature(signatureList: Signature[], signature: Signature, partialMatch: boolean, ignoreThisTypes: boolean, ignoreReturnTypes: boolean): Signature {
for (const s of signatureList) {
if (compareSignaturesIdentical(s, signature, partialMatch, ignoreReturnTypes, compareTypesIdentical)) {
if (compareSignaturesIdentical(s, signature, partialMatch, ignoreThisTypes, ignoreReturnTypes, compareTypesIdentical)) {
return s;
}
}
@ -3626,7 +3626,7 @@ namespace ts {
return undefined;
}
for (let i = 1; i < signatureLists.length; i++) {
if (!findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ false, /*ignoreReturnTypes*/ false)) {
if (!findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ false)) {
return undefined;
}
}
@ -3635,7 +3635,7 @@ namespace ts {
let result: Signature[] = undefined;
for (let i = 0; i < signatureLists.length; i++) {
// Allow matching non-generic signatures to have excess parameters and different return types
const match = i === listIndex ? signature : findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ true, /*ignoreReturnTypes*/ true);
const match = i === listIndex ? signature : findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ true, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true);
if (!match) {
return undefined;
}
@ -3656,13 +3656,16 @@ namespace ts {
for (let i = 0; i < signatureLists.length; i++) {
for (const signature of signatureLists[i]) {
// Only process signatures with parameter lists that aren't already in the result list
if (!result || !findMatchingSignature(result, signature, /*partialMatch*/ false, /*ignoreReturnTypes*/ true)) {
if (!result || !findMatchingSignature(result, signature, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true)) {
const unionSignatures = findMatchingSignatures(signatureLists, signature, i);
if (unionSignatures) {
let s = signature;
// Union the result types when more than one signature matches
if (unionSignatures.length > 1) {
s = cloneSignature(signature);
if (forEach(unionSignatures, sig => sig.thisType)) {
s.thisType = getUnionType(map(unionSignatures, sig => sig.thisType || anyType));
}
// Clear resolved return type we possibly got from cloneSignature
s.resolvedReturnType = undefined;
s.unionSignatures = unionSignatures;
@ -6002,7 +6005,7 @@ namespace ts {
}
let result = Ternary.True;
for (let i = 0, len = sourceSignatures.length; i < len; i++) {
const related = compareSignaturesIdentical(sourceSignatures[i], targetSignatures[i], /*partialMatch*/ false, /*ignoreReturnTypes*/ false, isRelatedTo);
const related = compareSignaturesIdentical(sourceSignatures[i], targetSignatures[i], /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ false, isRelatedTo);
if (!related) {
return Ternary.False;
}
@ -6203,7 +6206,7 @@ namespace ts {
/**
* See signatureRelatedTo, compareSignaturesIdentical
*/
function compareSignaturesIdentical(source: Signature, target: Signature, partialMatch: boolean, ignoreReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary {
function compareSignaturesIdentical(source: Signature, target: Signature, partialMatch: boolean, ignoreThisTypes: boolean, ignoreReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary {
// TODO (drosen): De-duplicate code between related functions.
if (source === target) {
return Ternary.True;
@ -6224,6 +6227,13 @@ namespace ts {
source = getErasedSignature(source);
target = getErasedSignature(target);
let result = Ternary.True;
if (!ignoreThisTypes && source.thisType && target.thisType) {
const related = compareTypes(source.thisType, target.thisType);
if (!related) {
return Ternary.False;
}
result &= related;
}
const targetLen = target.parameters.length;
for (let i = 0; i < targetLen; i++) {
const s = isRestParameterIndex(source, i) ? getRestTypeOfSignature(source) : getTypeOfSymbol(source.parameters[i]);
@ -8137,7 +8147,7 @@ namespace ts {
// This signature will contribute to contextual union signature
signatureList = [signature];
}
else if (!compareSignaturesIdentical(signatureList[0], signature, /*partialMatch*/ false, /*ignoreReturnTypes*/ true, compareTypesIdentical)) {
else if (!compareSignaturesIdentical(signatureList[0], signature, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true, compareTypesIdentical)) {
// Signatures aren't identical, do not use
return undefined;
}

View File

@ -0,0 +1,18 @@
//// [unionThisTypeInFunctions.ts]
interface Real {
method(n: number): void;
data: string;
}
interface Fake {
method(n: number): void;
data: number;
}
function test(r: Real | Fake) {
r.method(12);
}
//// [unionThisTypeInFunctions.js]
function test(r) {
r.method(12);
}

View File

@ -0,0 +1,33 @@
=== tests/cases/conformance/types/thisType/unionThisTypeInFunctions.ts ===
interface Real {
>Real : Symbol(Real, Decl(unionThisTypeInFunctions.ts, 0, 0))
method(n: number): void;
>method : Symbol(method, Decl(unionThisTypeInFunctions.ts, 0, 16))
>n : Symbol(n, Decl(unionThisTypeInFunctions.ts, 1, 11))
data: string;
>data : Symbol(data, Decl(unionThisTypeInFunctions.ts, 1, 28))
}
interface Fake {
>Fake : Symbol(Fake, Decl(unionThisTypeInFunctions.ts, 3, 1))
method(n: number): void;
>method : Symbol(method, Decl(unionThisTypeInFunctions.ts, 4, 16))
>n : Symbol(n, Decl(unionThisTypeInFunctions.ts, 5, 11))
data: number;
>data : Symbol(data, Decl(unionThisTypeInFunctions.ts, 5, 28))
}
function test(r: Real | Fake) {
>test : Symbol(test, Decl(unionThisTypeInFunctions.ts, 7, 1))
>r : Symbol(r, Decl(unionThisTypeInFunctions.ts, 8, 14))
>Real : Symbol(Real, Decl(unionThisTypeInFunctions.ts, 0, 0))
>Fake : Symbol(Fake, Decl(unionThisTypeInFunctions.ts, 3, 1))
r.method(12);
>r.method : Symbol(method, Decl(unionThisTypeInFunctions.ts, 0, 16), Decl(unionThisTypeInFunctions.ts, 4, 16))
>r : Symbol(r, Decl(unionThisTypeInFunctions.ts, 8, 14))
>method : Symbol(method, Decl(unionThisTypeInFunctions.ts, 0, 16), Decl(unionThisTypeInFunctions.ts, 4, 16))
}

View File

@ -0,0 +1,35 @@
=== tests/cases/conformance/types/thisType/unionThisTypeInFunctions.ts ===
interface Real {
>Real : Real
method(n: number): void;
>method : (this: this, n: number) => void
>n : number
data: string;
>data : string
}
interface Fake {
>Fake : Fake
method(n: number): void;
>method : (this: this, n: number) => void
>n : number
data: number;
>data : number
}
function test(r: Real | Fake) {
>test : (this: void, r: Real | Fake) => void
>r : Real | Fake
>Real : Real
>Fake : Fake
r.method(12);
>r.method(12) : void
>r.method : ((this: Real, n: number) => void) | ((this: Fake, n: number) => void)
>r : Real | Fake
>method : ((this: Real, n: number) => void) | ((this: Fake, n: number) => void)
>12 : number
}

View File

@ -0,0 +1,12 @@
// @strictThis: true
interface Real {
method(n: number): void;
data: string;
}
interface Fake {
method(n: number): void;
data: number;
}
function test(r: Real | Fake) {
r.method(12);
}