Use a comparison function instead of creating a new type for each signature.
This commit is contained in:
parent
2efa69773d
commit
d8db60a9a2
2 changed files with 94 additions and 33 deletions
|
@ -3501,7 +3501,7 @@ namespace ts {
|
|||
|
||||
function findMatchingSignature(signatureList: Signature[], signature: Signature, partialMatch: boolean, ignoreReturnTypes: boolean): Signature {
|
||||
for (const s of signatureList) {
|
||||
if (compareSignatures(s, signature, partialMatch, ignoreReturnTypes, compareTypes)) {
|
||||
if (compareSignaturesIdentical(s, signature, partialMatch, ignoreReturnTypes, compareTypesIdentical)) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
@ -4115,16 +4115,6 @@ namespace ts {
|
|||
return signature.erasedSignatureCache;
|
||||
}
|
||||
|
||||
function getAnyReturningErasedSignature(signature: Signature): Signature {
|
||||
if (!signature.anyReturningErasedSignatureCache) {
|
||||
const erasedSignature = getErasedSignature(signature);
|
||||
const anyReturningErasedSignature = cloneSignature(erasedSignature);
|
||||
anyReturningErasedSignature.resolvedReturnType = anyType;
|
||||
signature.anyReturningErasedSignatureCache = anyReturningErasedSignature;
|
||||
}
|
||||
return signature.anyReturningErasedSignatureCache;
|
||||
}
|
||||
|
||||
function getOrCreateTypeFromSignature(signature: Signature): ObjectType {
|
||||
// There are two ways to declare a construct signature, one is by declaring a class constructor
|
||||
// using the constructor keyword, and the other is declaring a bare construct signature in an
|
||||
|
@ -4965,7 +4955,7 @@ namespace ts {
|
|||
return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined);
|
||||
}
|
||||
|
||||
function compareTypes(source: Type, target: Type): Ternary {
|
||||
function compareTypesIdentical(source: Type, target: Type): Ternary {
|
||||
return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined) ? Ternary.True : Ternary.False;
|
||||
}
|
||||
|
||||
|
@ -4985,10 +4975,53 @@ namespace ts {
|
|||
return checkTypeRelatedTo(source, target, assignableRelation, errorNode, headMessage, containingMessageChain);
|
||||
}
|
||||
|
||||
function isSignatureAssignableTo(source: Signature, target: Signature): boolean {
|
||||
const sourceType = getOrCreateTypeFromSignature(source);
|
||||
const targetType = getOrCreateTypeFromSignature(target);
|
||||
return checkTypeRelatedTo(sourceType, targetType, assignableRelation, /*errorNode*/ undefined);
|
||||
/**
|
||||
* See signatureRelatedTo, compareSignaturesIdentical
|
||||
*/
|
||||
function isSignatureAssignableTo(source: Signature, target: Signature, ignoreReturnTypes: boolean): boolean {
|
||||
// TODO (drosen): De-duplicate code between related functions.
|
||||
if (source === target) {
|
||||
return true;
|
||||
}
|
||||
if (!target.hasRestParameter && source.minArgumentCount > target.parameters.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Spec 1.0 Section 3.8.3 & 3.8.4:
|
||||
// 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);
|
||||
target = getErasedSignature(target);
|
||||
|
||||
const sourceMax = getNumNonRestParameters(source);
|
||||
const targetMax = getNumNonRestParameters(target);
|
||||
const checkCount = getNumParametersToCheckForSignatureRelatability(source, sourceMax, target, targetMax);
|
||||
for (let i = 0; i < checkCount; i++) {
|
||||
const s = i < sourceMax ? getTypeOfSymbol(source.parameters[i]) : getRestTypeOfSignature(source);
|
||||
const t = i < targetMax ? getTypeOfSymbol(target.parameters[i]) : getRestTypeOfSignature(target);
|
||||
const related = isTypeAssignableTo(t, s) || isTypeAssignableTo(s, t);
|
||||
if (!related) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignoreReturnTypes) {
|
||||
const targetReturnType = getReturnTypeOfSignature(target);
|
||||
if (targetReturnType === voidType) {
|
||||
return true;
|
||||
}
|
||||
const sourceReturnType = getReturnTypeOfSignature(source);
|
||||
|
||||
// The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions
|
||||
if (targetReturnType.flags & TypeFlags.PredicateType && (targetReturnType as PredicateType).predicate.kind === TypePredicateKind.Identifier) {
|
||||
if (!(sourceReturnType.flags & TypeFlags.PredicateType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return isTypeAssignableTo(sourceReturnType, targetReturnType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isImplementationCompatibleWithOverload(implementation: Signature, overload: Signature): boolean {
|
||||
|
@ -5002,15 +5035,35 @@ namespace ts {
|
|||
|| checkTypeRelatedTo(targetReturnType, sourceReturnType, assignableRelation, /*errorNode*/ undefined)
|
||||
|| checkTypeRelatedTo(sourceReturnType, targetReturnType, assignableRelation, /*errorNode*/ undefined)) {
|
||||
|
||||
// The return types are compatible, so create versions of the signature with 'any' as the return type.
|
||||
// We need to do this so that we can check assignability while disregarding the return type.
|
||||
const anyReturningSource = getAnyReturningErasedSignature(implementation);
|
||||
const anyReturningTarget = getAnyReturningErasedSignature(overload);
|
||||
return isSignatureAssignableTo(erasedSource, erasedTarget, /*ignoreReturnTypes*/ true);
|
||||
}
|
||||
|
||||
// Create object types to actually perform relation checks.
|
||||
const anyReturningSourceType = getOrCreateTypeFromSignature(anyReturningSource);
|
||||
const anyReturningTargetType = getOrCreateTypeFromSignature(anyReturningTarget);
|
||||
return checkTypeRelatedTo(anyReturningSourceType, anyReturningTargetType, assignableRelation, /*errorNode*/ undefined);
|
||||
return false;
|
||||
}
|
||||
|
||||
function getNumNonRestParameters(signature: Signature) {
|
||||
const numParams = signature.parameters.length;
|
||||
return signature.hasRestParameter ?
|
||||
numParams - 1 :
|
||||
numParams;
|
||||
}
|
||||
|
||||
function getNumParametersToCheckForSignatureRelatability(source: Signature, sourceNonRestParamCount: number, target: Signature, targetNonRestParamCount: number) {
|
||||
if (source.hasRestParameter === target.hasRestParameter) {
|
||||
if (source.hasRestParameter) {
|
||||
// If both have rest parameters, get the max and add 1 to
|
||||
// compensate for the rest parameter.
|
||||
return Math.max(sourceNonRestParamCount, targetNonRestParamCount) + 1;
|
||||
}
|
||||
else {
|
||||
return Math.min(sourceNonRestParamCount, targetNonRestParamCount);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Return the count for whichever signature doesn't have rest parameters.
|
||||
return source.hasRestParameter ?
|
||||
targetNonRestParamCount :
|
||||
sourceNonRestParamCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5645,7 +5698,11 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See signatureAssignableTo, signatureAssignableTo
|
||||
*/
|
||||
function signatureRelatedTo(source: Signature, target: Signature, reportErrors: boolean): Ternary {
|
||||
// TODO (drosen): De-duplicate code between related functions.
|
||||
if (source === target) {
|
||||
return Ternary.True;
|
||||
}
|
||||
|
@ -5697,10 +5754,12 @@ namespace ts {
|
|||
}
|
||||
|
||||
const targetReturnType = getReturnTypeOfSignature(target);
|
||||
if (targetReturnType === voidType) return result;
|
||||
if (targetReturnType === voidType) {
|
||||
return result;
|
||||
}
|
||||
const sourceReturnType = getReturnTypeOfSignature(source);
|
||||
|
||||
// The follow block preserves old behavior forbidding boolean returning functions from being assignable to type guard returning functions
|
||||
// The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions
|
||||
if (targetReturnType.flags & TypeFlags.PredicateType && (targetReturnType as PredicateType).predicate.kind === TypePredicateKind.Identifier) {
|
||||
if (!(sourceReturnType.flags & TypeFlags.PredicateType)) {
|
||||
if (reportErrors) {
|
||||
|
@ -5721,7 +5780,7 @@ namespace ts {
|
|||
}
|
||||
let result = Ternary.True;
|
||||
for (let i = 0, len = sourceSignatures.length; i < len; ++i) {
|
||||
const related = compareSignatures(sourceSignatures[i], targetSignatures[i], /*partialMatch*/ false, /*ignoreReturnTypes*/ false, isRelatedTo);
|
||||
const related = compareSignaturesIdentical(sourceSignatures[i], targetSignatures[i], /*partialMatch*/ false, /*ignoreReturnTypes*/ false, isRelatedTo);
|
||||
if (!related) {
|
||||
return Ternary.False;
|
||||
}
|
||||
|
@ -5833,7 +5892,7 @@ namespace ts {
|
|||
}
|
||||
|
||||
function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean {
|
||||
return compareProperties(sourceProp, targetProp, compareTypes) !== Ternary.False;
|
||||
return compareProperties(sourceProp, targetProp, compareTypesIdentical) !== Ternary.False;
|
||||
}
|
||||
|
||||
function compareProperties(sourceProp: Symbol, targetProp: Symbol, compareTypes: (source: Type, target: Type) => Ternary): Ternary {
|
||||
|
@ -5880,7 +5939,11 @@ namespace ts {
|
|||
return false;
|
||||
}
|
||||
|
||||
function compareSignatures(source: Signature, target: Signature, partialMatch: boolean, ignoreReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary {
|
||||
/**
|
||||
* See signatureRelatedTo, compareSignaturesIdentical
|
||||
*/
|
||||
function compareSignaturesIdentical(source: Signature, target: Signature, partialMatch: boolean, ignoreReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary {
|
||||
// TODO (drosen): De-duplicate code between related functions.
|
||||
if (source === target) {
|
||||
return Ternary.True;
|
||||
}
|
||||
|
@ -7571,7 +7634,7 @@ namespace ts {
|
|||
// This signature will contribute to contextual union signature
|
||||
signatureList = [signature];
|
||||
}
|
||||
else if (!compareSignatures(signatureList[0], signature, /*partialMatch*/ false, /*ignoreReturnTypes*/ true, compareTypes)) {
|
||||
else if (!compareSignaturesIdentical(signatureList[0], signature, /*partialMatch*/ false, /*ignoreReturnTypes*/ true, compareTypesIdentical)) {
|
||||
// Signatures aren't identical, do not use
|
||||
return undefined;
|
||||
}
|
||||
|
@ -11564,7 +11627,7 @@ namespace ts {
|
|||
}
|
||||
|
||||
for (const otherSignature of signaturesToCheck) {
|
||||
if (!otherSignature.hasStringLiterals && isSignatureAssignableTo(signature, otherSignature)) {
|
||||
if (!otherSignature.hasStringLiterals && isSignatureAssignableTo(signature, otherSignature, /*ignoreReturnTypes*/ false)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2280,8 +2280,6 @@ namespace ts {
|
|||
/* @internal */
|
||||
erasedSignatureCache?: Signature; // Erased version of signature (deferred)
|
||||
/* @internal */
|
||||
anyReturningErasedSignatureCache?: Signature; // A version of the erased signature whose type returns 'any'
|
||||
/* @internal */
|
||||
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue