Skip to content

Commit d8db60a

Browse files
Use a comparison function instead of creating a new type for each signature.
1 parent 2efa697 commit d8db60a

File tree

2 files changed

+94
-33
lines changed

2 files changed

+94
-33
lines changed

src/compiler/checker.ts

Lines changed: 94 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3501,7 +3501,7 @@ namespace ts {
35013501

35023502
function findMatchingSignature(signatureList: Signature[], signature: Signature, partialMatch: boolean, ignoreReturnTypes: boolean): Signature {
35033503
for (const s of signatureList) {
3504-
if (compareSignatures(s, signature, partialMatch, ignoreReturnTypes, compareTypes)) {
3504+
if (compareSignaturesIdentical(s, signature, partialMatch, ignoreReturnTypes, compareTypesIdentical)) {
35053505
return s;
35063506
}
35073507
}
@@ -4115,16 +4115,6 @@ namespace ts {
41154115
return signature.erasedSignatureCache;
41164116
}
41174117

4118-
function getAnyReturningErasedSignature(signature: Signature): Signature {
4119-
if (!signature.anyReturningErasedSignatureCache) {
4120-
const erasedSignature = getErasedSignature(signature);
4121-
const anyReturningErasedSignature = cloneSignature(erasedSignature);
4122-
anyReturningErasedSignature.resolvedReturnType = anyType;
4123-
signature.anyReturningErasedSignatureCache = anyReturningErasedSignature;
4124-
}
4125-
return signature.anyReturningErasedSignatureCache;
4126-
}
4127-
41284118
function getOrCreateTypeFromSignature(signature: Signature): ObjectType {
41294119
// There are two ways to declare a construct signature, one is by declaring a class constructor
41304120
// using the constructor keyword, and the other is declaring a bare construct signature in an
@@ -4965,7 +4955,7 @@ namespace ts {
49654955
return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined);
49664956
}
49674957

4968-
function compareTypes(source: Type, target: Type): Ternary {
4958+
function compareTypesIdentical(source: Type, target: Type): Ternary {
49694959
return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined) ? Ternary.True : Ternary.False;
49704960
}
49714961

@@ -4985,10 +4975,53 @@ namespace ts {
49854975
return checkTypeRelatedTo(source, target, assignableRelation, errorNode, headMessage, containingMessageChain);
49864976
}
49874977

4988-
function isSignatureAssignableTo(source: Signature, target: Signature): boolean {
4989-
const sourceType = getOrCreateTypeFromSignature(source);
4990-
const targetType = getOrCreateTypeFromSignature(target);
4991-
return checkTypeRelatedTo(sourceType, targetType, assignableRelation, /*errorNode*/ undefined);
4978+
/**
4979+
* See signatureRelatedTo, compareSignaturesIdentical
4980+
*/
4981+
function isSignatureAssignableTo(source: Signature, target: Signature, ignoreReturnTypes: boolean): boolean {
4982+
// TODO (drosen): De-duplicate code between related functions.
4983+
if (source === target) {
4984+
return true;
4985+
}
4986+
if (!target.hasRestParameter && source.minArgumentCount > target.parameters.length) {
4987+
return false;
4988+
}
4989+
4990+
// Spec 1.0 Section 3.8.3 & 3.8.4:
4991+
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
4992+
source = getErasedSignature(source);
4993+
target = getErasedSignature(target);
4994+
4995+
const sourceMax = getNumNonRestParameters(source);
4996+
const targetMax = getNumNonRestParameters(target);
4997+
const checkCount = getNumParametersToCheckForSignatureRelatability(source, sourceMax, target, targetMax);
4998+
for (let i = 0; i < checkCount; i++) {
4999+
const s = i < sourceMax ? getTypeOfSymbol(source.parameters[i]) : getRestTypeOfSignature(source);
5000+
const t = i < targetMax ? getTypeOfSymbol(target.parameters[i]) : getRestTypeOfSignature(target);
5001+
const related = isTypeAssignableTo(t, s) || isTypeAssignableTo(s, t);
5002+
if (!related) {
5003+
return false;
5004+
}
5005+
}
5006+
5007+
if (!ignoreReturnTypes) {
5008+
const targetReturnType = getReturnTypeOfSignature(target);
5009+
if (targetReturnType === voidType) {
5010+
return true;
5011+
}
5012+
const sourceReturnType = getReturnTypeOfSignature(source);
5013+
5014+
// The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions
5015+
if (targetReturnType.flags & TypeFlags.PredicateType && (targetReturnType as PredicateType).predicate.kind === TypePredicateKind.Identifier) {
5016+
if (!(sourceReturnType.flags & TypeFlags.PredicateType)) {
5017+
return false;
5018+
}
5019+
}
5020+
5021+
return isTypeAssignableTo(sourceReturnType, targetReturnType);
5022+
}
5023+
5024+
return true;
49925025
}
49935026

49945027
function isImplementationCompatibleWithOverload(implementation: Signature, overload: Signature): boolean {
@@ -5002,15 +5035,35 @@ namespace ts {
50025035
|| checkTypeRelatedTo(targetReturnType, sourceReturnType, assignableRelation, /*errorNode*/ undefined)
50035036
|| checkTypeRelatedTo(sourceReturnType, targetReturnType, assignableRelation, /*errorNode*/ undefined)) {
50045037

5005-
// The return types are compatible, so create versions of the signature with 'any' as the return type.
5006-
// We need to do this so that we can check assignability while disregarding the return type.
5007-
const anyReturningSource = getAnyReturningErasedSignature(implementation);
5008-
const anyReturningTarget = getAnyReturningErasedSignature(overload);
5038+
return isSignatureAssignableTo(erasedSource, erasedTarget, /*ignoreReturnTypes*/ true);
5039+
}
50095040

5010-
// Create object types to actually perform relation checks.
5011-
const anyReturningSourceType = getOrCreateTypeFromSignature(anyReturningSource);
5012-
const anyReturningTargetType = getOrCreateTypeFromSignature(anyReturningTarget);
5013-
return checkTypeRelatedTo(anyReturningSourceType, anyReturningTargetType, assignableRelation, /*errorNode*/ undefined);
5041+
return false;
5042+
}
5043+
5044+
function getNumNonRestParameters(signature: Signature) {
5045+
const numParams = signature.parameters.length;
5046+
return signature.hasRestParameter ?
5047+
numParams - 1 :
5048+
numParams;
5049+
}
5050+
5051+
function getNumParametersToCheckForSignatureRelatability(source: Signature, sourceNonRestParamCount: number, target: Signature, targetNonRestParamCount: number) {
5052+
if (source.hasRestParameter === target.hasRestParameter) {
5053+
if (source.hasRestParameter) {
5054+
// If both have rest parameters, get the max and add 1 to
5055+
// compensate for the rest parameter.
5056+
return Math.max(sourceNonRestParamCount, targetNonRestParamCount) + 1;
5057+
}
5058+
else {
5059+
return Math.min(sourceNonRestParamCount, targetNonRestParamCount);
5060+
}
5061+
}
5062+
else {
5063+
// Return the count for whichever signature doesn't have rest parameters.
5064+
return source.hasRestParameter ?
5065+
targetNonRestParamCount :
5066+
sourceNonRestParamCount;
50145067
}
50155068
}
50165069

@@ -5645,7 +5698,11 @@ namespace ts {
56455698
}
56465699
}
56475700

5701+
/**
5702+
* See signatureAssignableTo, signatureAssignableTo
5703+
*/
56485704
function signatureRelatedTo(source: Signature, target: Signature, reportErrors: boolean): Ternary {
5705+
// TODO (drosen): De-duplicate code between related functions.
56495706
if (source === target) {
56505707
return Ternary.True;
56515708
}
@@ -5697,10 +5754,12 @@ namespace ts {
56975754
}
56985755

56995756
const targetReturnType = getReturnTypeOfSignature(target);
5700-
if (targetReturnType === voidType) return result;
5757+
if (targetReturnType === voidType) {
5758+
return result;
5759+
}
57015760
const sourceReturnType = getReturnTypeOfSignature(source);
57025761

5703-
// The follow block preserves old behavior forbidding boolean returning functions from being assignable to type guard returning functions
5762+
// The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions
57045763
if (targetReturnType.flags & TypeFlags.PredicateType && (targetReturnType as PredicateType).predicate.kind === TypePredicateKind.Identifier) {
57055764
if (!(sourceReturnType.flags & TypeFlags.PredicateType)) {
57065765
if (reportErrors) {
@@ -5721,7 +5780,7 @@ namespace ts {
57215780
}
57225781
let result = Ternary.True;
57235782
for (let i = 0, len = sourceSignatures.length; i < len; ++i) {
5724-
const related = compareSignatures(sourceSignatures[i], targetSignatures[i], /*partialMatch*/ false, /*ignoreReturnTypes*/ false, isRelatedTo);
5783+
const related = compareSignaturesIdentical(sourceSignatures[i], targetSignatures[i], /*partialMatch*/ false, /*ignoreReturnTypes*/ false, isRelatedTo);
57255784
if (!related) {
57265785
return Ternary.False;
57275786
}
@@ -5833,7 +5892,7 @@ namespace ts {
58335892
}
58345893

58355894
function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean {
5836-
return compareProperties(sourceProp, targetProp, compareTypes) !== Ternary.False;
5895+
return compareProperties(sourceProp, targetProp, compareTypesIdentical) !== Ternary.False;
58375896
}
58385897

58395898
function compareProperties(sourceProp: Symbol, targetProp: Symbol, compareTypes: (source: Type, target: Type) => Ternary): Ternary {
@@ -5880,7 +5939,11 @@ namespace ts {
58805939
return false;
58815940
}
58825941

5883-
function compareSignatures(source: Signature, target: Signature, partialMatch: boolean, ignoreReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary {
5942+
/**
5943+
* See signatureRelatedTo, compareSignaturesIdentical
5944+
*/
5945+
function compareSignaturesIdentical(source: Signature, target: Signature, partialMatch: boolean, ignoreReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary {
5946+
// TODO (drosen): De-duplicate code between related functions.
58845947
if (source === target) {
58855948
return Ternary.True;
58865949
}
@@ -7571,7 +7634,7 @@ namespace ts {
75717634
// This signature will contribute to contextual union signature
75727635
signatureList = [signature];
75737636
}
7574-
else if (!compareSignatures(signatureList[0], signature, /*partialMatch*/ false, /*ignoreReturnTypes*/ true, compareTypes)) {
7637+
else if (!compareSignaturesIdentical(signatureList[0], signature, /*partialMatch*/ false, /*ignoreReturnTypes*/ true, compareTypesIdentical)) {
75757638
// Signatures aren't identical, do not use
75767639
return undefined;
75777640
}
@@ -11564,7 +11627,7 @@ namespace ts {
1156411627
}
1156511628

1156611629
for (const otherSignature of signaturesToCheck) {
11567-
if (!otherSignature.hasStringLiterals && isSignatureAssignableTo(signature, otherSignature)) {
11630+
if (!otherSignature.hasStringLiterals && isSignatureAssignableTo(signature, otherSignature, /*ignoreReturnTypes*/ false)) {
1156811631
return;
1156911632
}
1157011633
}

src/compiler/types.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2280,8 +2280,6 @@ namespace ts {
22802280
/* @internal */
22812281
erasedSignatureCache?: Signature; // Erased version of signature (deferred)
22822282
/* @internal */
2283-
anyReturningErasedSignatureCache?: Signature; // A version of the erased signature whose type returns 'any'
2284-
/* @internal */
22852283
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
22862284
}
22872285

0 commit comments

Comments
 (0)