Skip to content

Commit df50477

Browse files
committed
Improve checking of instantiations of same signature
1 parent 78f54c5 commit df50477

File tree

1 file changed

+23
-21
lines changed

1 file changed

+23
-21
lines changed

src/compiler/checker.ts

+23-21
Original file line numberDiff line numberDiff line change
@@ -8550,11 +8550,16 @@ namespace ts {
85508550
isInJSFile(signature.declaration));
85518551
}
85528552

8553+
function getErasedConstraint(type: Type, typeParameters: readonly TypeParameter[]): Type | undefined {
8554+
const constraint = getConstraintOfType(type);
8555+
return constraint && contains(typeParameters, constraint) ? getErasedConstraint(constraint, typeParameters) : constraint;
8556+
}
8557+
85538558
function getBaseSignature(signature: Signature) {
85548559
const typeParameters = signature.typeParameters;
85558560
if (typeParameters) {
85568561
const typeEraser = createTypeEraser(typeParameters);
8557-
const baseConstraints = map(typeParameters, tp => instantiateType(getBaseConstraintOfType(tp), typeEraser) || unknownType);
8562+
const baseConstraints = map(typeParameters, tp => instantiateType(getErasedConstraint(tp, typeParameters), typeEraser) || unknownType);
85588563
return instantiateSignature(signature, createTypeMapper(typeParameters, baseConstraints), /*eraseTypeParameters*/ true);
85598564
}
85608565
return signature;
@@ -13392,35 +13397,33 @@ namespace ts {
1339213397
let result = Ternary.True;
1339313398
const saveErrorInfo = errorInfo;
1339413399

13395-
if (getObjectFlags(source) & ObjectFlags.Instantiated && getObjectFlags(target) & ObjectFlags.Instantiated && source.symbol === target.symbol) {
13396-
// We have instantiations of the same anonymous type (which typically will be the type of a
13397-
// method). Simply do a pairwise comparison of the signatures in the two signature lists instead
13398-
// of the much more expensive N * M comparison matrix we explore below. We instantiate type
13399-
// parameters to their constraints because, whereas the type parameters are known to be the same,
13400-
// the constraints might differ if they reference outer type parameters.
13400+
const sameSignatureInstantiations = getObjectFlags(source) & ObjectFlags.Instantiated && getObjectFlags(target) & ObjectFlags.Instantiated && source.symbol === target.symbol;
13401+
if (sameSignatureInstantiations || sourceSignatures.length === 1 && targetSignatures.length === 1) {
13402+
// We have instantiations of the same anonymous type (which typically will be the type of a method)
13403+
// or we have non-overloaded signatures. Simply do a pairwise comparison of the signatures in the
13404+
// two signature lists instead of the much more expensive N * M comparison matrix we explore below.
13405+
const eraseGenerics = relation === comparableRelation || !!compilerOptions.noStrictGenericChecks;
1340113406
for (let i = 0; i < targetSignatures.length; i++) {
13402-
const related = signatureRelatedTo(getBaseSignature(sourceSignatures[i]), getBaseSignature(targetSignatures[i]), /*erase*/ false, reportErrors);
13407+
const s = sourceSignatures[i];
13408+
const t = targetSignatures[i];
13409+
// We erase type parameters for the comparable relation or when strict checks are disabled.
13410+
// Otherwise, when we have instantiations of the same anonymous type, we instantiate target
13411+
// type parameters to their constraints because the type parameters are known to be the same.
13412+
const effectiveSource = eraseGenerics ? getErasedSignature(s) : s;
13413+
const effectiveTarget = eraseGenerics ? getErasedSignature(t) : sameSignatureInstantiations ? getBaseSignature(t) : t;
13414+
const related = signatureRelatedTo(effectiveSource, effectiveTarget, reportErrors);
1340313415
if (!related) {
1340413416
return Ternary.False;
1340513417
}
1340613418
result &= related;
1340713419
}
1340813420
}
13409-
else if (sourceSignatures.length === 1 && targetSignatures.length === 1) {
13410-
// For simple functions (functions with a single signature) we only erase type parameters for
13411-
// the comparable relation. Otherwise, if the source signature is generic, we instantiate it
13412-
// in the context of the target signature before checking the relationship. Ideally we'd do
13413-
// this regardless of the number of signatures, but the potential costs are prohibitive due
13414-
// to the quadratic nature of the logic below.
13415-
const eraseGenerics = relation === comparableRelation || !!compilerOptions.noStrictGenericChecks;
13416-
result = signatureRelatedTo(sourceSignatures[0], targetSignatures[0], eraseGenerics, reportErrors);
13417-
}
1341813421
else {
1341913422
outer: for (const t of targetSignatures) {
1342013423
// Only elaborate errors from the first failure
1342113424
let shouldElaborateErrors = reportErrors;
1342213425
for (const s of sourceSignatures) {
13423-
const related = signatureRelatedTo(s, t, /*erase*/ true, shouldElaborateErrors);
13426+
const related = signatureRelatedTo(getErasedSignature(s), getErasedSignature(t), shouldElaborateErrors);
1342413427
if (related) {
1342513428
result &= related;
1342613429
errorInfo = saveErrorInfo;
@@ -13443,9 +13446,8 @@ namespace ts {
1344313446
/**
1344413447
* See signatureAssignableTo, compareSignaturesIdentical
1344513448
*/
13446-
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean): Ternary {
13447-
return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
13448-
CallbackCheck.None, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo);
13449+
function signatureRelatedTo(source: Signature, target: Signature, reportErrors: boolean): Ternary {
13450+
return compareSignaturesRelated(source, target, CallbackCheck.None, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo);
1344913451
}
1345013452

1345113453
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {

0 commit comments

Comments
 (0)