Allow partial matches in union type signatures
This commit is contained in:
parent
2b8ef5e6fd
commit
b3dd18a463
1 changed files with 60 additions and 45 deletions
|
@ -3131,52 +3131,66 @@ namespace ts {
|
||||||
setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexType, arrayType.numberIndexType);
|
setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexType, arrayType.numberIndexType);
|
||||||
}
|
}
|
||||||
|
|
||||||
function findMatchingSignature(signature: Signature, signatureList: Signature[]): Signature {
|
function findMatchingSignature(signatureList: Signature[], signature: Signature, partialMatch: boolean, ignoreReturnTypes: boolean): Signature {
|
||||||
for (let s of signatureList) {
|
for (let s of signatureList) {
|
||||||
// Only signatures with no type parameters may differ in return types
|
if (compareSignatures(s, signature, partialMatch, ignoreReturnTypes, compareTypes)) {
|
||||||
if (compareSignatures(signature, s, /*compareReturnTypes*/ !!signature.typeParameters, compareTypes)) {
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function findMatchingSignatures(signature: Signature, signatureLists: Signature[][]): Signature[] {
|
function findMatchingSignatures(signatureLists: Signature[][], signature: Signature, listIndex: number): Signature[] {
|
||||||
let result: Signature[] = undefined;
|
if (signature.typeParameters) {
|
||||||
|
// We require an exact match for generic signatures, so we only return signatures from the first
|
||||||
|
// signature list and only if they have exact matches in the other signature lists.
|
||||||
|
if (listIndex > 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
for (let i = 1; i < signatureLists.length; i++) {
|
for (let i = 1; i < signatureLists.length; i++) {
|
||||||
let match = findMatchingSignature(signature, signatureLists[i]);
|
if (!findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ false, /*ignoreReturnTypes*/ false)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [signature];
|
||||||
|
}
|
||||||
|
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
|
||||||
|
let match = i === listIndex ? signature : findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ true, /*ignoreReturnTypes*/ true);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (!result) {
|
if (!contains(result, match)) {
|
||||||
result = [signature];
|
(result || (result = [])).push(match);
|
||||||
}
|
|
||||||
if (match !== signature) {
|
|
||||||
result.push(match);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The signatures of a union type are those signatures that are present and identical in each of the
|
// The signatures of a union type are those signatures that are present in each of the constituent types.
|
||||||
// constituent types, except that non-generic signatures may differ in return types. When signatures
|
// Generic signatures must match exactly, but non-generic signatures are allowed to have extra optional
|
||||||
// differ in return types, the resulting return type is the union of the constituent return types.
|
// parameters and may differ in return types. When signatures differ in return types, the resulting return
|
||||||
|
// type is the union of the constituent return types.
|
||||||
function getUnionSignatures(types: Type[], kind: SignatureKind): Signature[] {
|
function getUnionSignatures(types: Type[], kind: SignatureKind): Signature[] {
|
||||||
let signatureLists = map(types, t => getSignaturesOfType(t, kind));
|
let signatureLists = map(types, t => getSignaturesOfType(t, kind));
|
||||||
let result: Signature[] = undefined;
|
let result: Signature[] = undefined;
|
||||||
for (let source of signatureLists[0]) {
|
for (let i = 0; i < signatureLists.length; i++) {
|
||||||
let unionSignatures = findMatchingSignatures(source, signatureLists);
|
for (let 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)) {
|
||||||
|
let unionSignatures = findMatchingSignatures(signatureLists, signature, i);
|
||||||
if (unionSignatures) {
|
if (unionSignatures) {
|
||||||
let signature: Signature = undefined;
|
let s = signature;
|
||||||
if (unionSignatures.length === 1 || source.typeParameters) {
|
// Union the result types when more than one signature matches
|
||||||
signature = source;
|
if (unionSignatures.length > 1) {
|
||||||
}
|
s = cloneSignature(signature);
|
||||||
else {
|
|
||||||
signature = cloneSignature(source);
|
|
||||||
// Clear resolved return type we possibly got from cloneSignature
|
// Clear resolved return type we possibly got from cloneSignature
|
||||||
signature.resolvedReturnType = undefined;
|
s.resolvedReturnType = undefined;
|
||||||
signature.unionSignatures = unionSignatures;
|
s.unionSignatures = unionSignatures;
|
||||||
|
}
|
||||||
|
(result || (result = [])).push(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(result || (result = [])).push(signature);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result || emptyArray;
|
return result || emptyArray;
|
||||||
|
@ -5242,7 +5256,7 @@ namespace ts {
|
||||||
}
|
}
|
||||||
let result = Ternary.True;
|
let result = Ternary.True;
|
||||||
for (let i = 0, len = sourceSignatures.length; i < len; ++i) {
|
for (let i = 0, len = sourceSignatures.length; i < len; ++i) {
|
||||||
let related = compareSignatures(sourceSignatures[i], targetSignatures[i], /*compareReturnTypes*/ true, isRelatedTo);
|
let related = compareSignatures(sourceSignatures[i], targetSignatures[i], /*partialMatch*/ false, /*ignoreReturnTypes*/ false, isRelatedTo);
|
||||||
if (!related) {
|
if (!related) {
|
||||||
return Ternary.False;
|
return Ternary.False;
|
||||||
}
|
}
|
||||||
|
@ -5372,15 +5386,21 @@ namespace ts {
|
||||||
return compareTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp));
|
return compareTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp));
|
||||||
}
|
}
|
||||||
|
|
||||||
function compareSignatures(source: Signature, target: Signature, compareReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary {
|
function compareSignatures(source: Signature, target: Signature, partialMatch: boolean, ignoreReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary {
|
||||||
if (source === target) {
|
if (source === target) {
|
||||||
return Ternary.True;
|
return Ternary.True;
|
||||||
}
|
}
|
||||||
if (source.parameters.length !== target.parameters.length ||
|
if (source.minArgumentCount !== target.minArgumentCount) {
|
||||||
source.minArgumentCount !== target.minArgumentCount ||
|
|
||||||
source.hasRestParameter !== target.hasRestParameter) {
|
|
||||||
return Ternary.False;
|
return Ternary.False;
|
||||||
}
|
}
|
||||||
|
if (source.parameters.length !== target.parameters.length ||
|
||||||
|
source.hasRestParameter !== target.hasRestParameter) {
|
||||||
|
if (!partialMatch ||
|
||||||
|
source.parameters.length < target.parameters.length ||
|
||||||
|
target.hasRestParameter) {
|
||||||
|
return Ternary.False;
|
||||||
|
}
|
||||||
|
}
|
||||||
let result = Ternary.True;
|
let result = Ternary.True;
|
||||||
if (source.typeParameters && target.typeParameters) {
|
if (source.typeParameters && target.typeParameters) {
|
||||||
if (source.typeParameters.length !== target.typeParameters.length) {
|
if (source.typeParameters.length !== target.typeParameters.length) {
|
||||||
|
@ -5401,16 +5421,18 @@ namespace ts {
|
||||||
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
|
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
|
||||||
source = getErasedSignature(source);
|
source = getErasedSignature(source);
|
||||||
target = getErasedSignature(target);
|
target = getErasedSignature(target);
|
||||||
for (let i = 0, len = source.parameters.length; i < len; i++) {
|
let sourceLen = source.parameters.length;
|
||||||
let s = source.hasRestParameter && i === len - 1 ? getRestTypeOfSignature(source) : getTypeOfSymbol(source.parameters[i]);
|
let targetLen = target.parameters.length;
|
||||||
let t = target.hasRestParameter && i === len - 1 ? getRestTypeOfSignature(target) : getTypeOfSymbol(target.parameters[i]);
|
for (let i = 0; i < targetLen; i++) {
|
||||||
|
let s = source.hasRestParameter && i === sourceLen - 1 ? getRestTypeOfSignature(source) : getTypeOfSymbol(source.parameters[i]);
|
||||||
|
let t = target.hasRestParameter && i === targetLen - 1 ? getRestTypeOfSignature(target) : getTypeOfSymbol(target.parameters[i]);
|
||||||
let related = compareTypes(s, t);
|
let related = compareTypes(s, t);
|
||||||
if (!related) {
|
if (!related) {
|
||||||
return Ternary.False;
|
return Ternary.False;
|
||||||
}
|
}
|
||||||
result &= related;
|
result &= related;
|
||||||
}
|
}
|
||||||
if (compareReturnTypes) {
|
if (!ignoreReturnTypes) {
|
||||||
result &= compareTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target));
|
result &= compareTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -6924,20 +6946,13 @@ namespace ts {
|
||||||
let signatureList: Signature[];
|
let signatureList: Signature[];
|
||||||
let types = (<UnionType>type).types;
|
let types = (<UnionType>type).types;
|
||||||
for (let current of types) {
|
for (let current of types) {
|
||||||
// The signature set of all constituent type with call signatures should match
|
|
||||||
// So number of signatures allowed is either 0 or 1
|
|
||||||
if (signatureList &&
|
|
||||||
getSignaturesOfStructuredType(current, SignatureKind.Call).length > 1) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
let signature = getNonGenericSignature(current);
|
let signature = getNonGenericSignature(current);
|
||||||
if (signature) {
|
if (signature) {
|
||||||
if (!signatureList) {
|
if (!signatureList) {
|
||||||
// This signature will contribute to contextual union signature
|
// This signature will contribute to contextual union signature
|
||||||
signatureList = [signature];
|
signatureList = [signature];
|
||||||
}
|
}
|
||||||
else if (!compareSignatures(signatureList[0], signature, /*compareReturnTypes*/ false, compareTypes)) {
|
else if (!compareSignatures(signatureList[0], signature, /*partialMatch*/ false, /*ignoreReturnTypes*/ true, compareTypes)) {
|
||||||
// Signatures aren't identical, do not use
|
// Signatures aren't identical, do not use
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue