Skip to content

Commit 368e5af

Browse files
committed
Stricter type relationship checking of generic signatures
1 parent ccc60c8 commit 368e5af

File tree

1 file changed

+25
-13
lines changed

1 file changed

+25
-13
lines changed

src/compiler/checker.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8408,10 +8408,9 @@ namespace ts {
84088408
return Ternary.False;
84098409
}
84108410

8411-
// Spec 1.0 Section 3.8.3 & 3.8.4:
8412-
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
8413-
source = getErasedSignature(source);
8414-
target = getErasedSignature(target);
8411+
if (source.typeParameters) {
8412+
source = instantiateSignatureInContextOf(source, target);
8413+
}
84158414

84168415
let result = Ternary.True;
84178416

@@ -9446,23 +9445,32 @@ namespace ts {
94469445
const saveErrorInfo = errorInfo;
94479446

94489447
if (getObjectFlags(source) & ObjectFlags.Instantiated && getObjectFlags(target) & ObjectFlags.Instantiated && source.symbol === target.symbol) {
9449-
// We instantiations of the same anonymous type (which typically will be the type of a method).
9450-
// Simply do a pairwise comparison of the signatures in the two signature lists instead of the
9451-
// much more expensive N * M comparison matrix we explore below.
9448+
// We have instantiations of the same anonymous type (which typically will be the type of a
9449+
// method). Simply do a pairwise comparison of the signatures in the two signature lists instead
9450+
// of the much more expensive N * M comparison matrix we explore below. We erase type parameters
9451+
// as they are known to always be the same.
94529452
for (let i = 0; i < targetSignatures.length; i++) {
9453-
const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], reportErrors);
9453+
const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], /*erase*/ true, reportErrors);
94549454
if (!related) {
94559455
return Ternary.False;
94569456
}
94579457
result &= related;
94589458
}
94599459
}
9460+
else if (sourceSignatures.length === 1 && targetSignatures.length === 1) {
9461+
// For pure functions (functions with a single signature) we only erase type parameters for
9462+
// the comparable relation. Otherwise, if the source signature is generic, we instantiate it
9463+
// in the context of the target signature before checking the relationship. Ideally we'd do
9464+
// this regardless of the number of signatures, but the potential costs are prohibitive due
9465+
// to the quadratic nature of the logic below.
9466+
result = signatureRelatedTo(sourceSignatures[0], targetSignatures[0], /*erase*/ relation === comparableRelation, reportErrors);
9467+
}
94609468
else {
94619469
outer: for (const t of targetSignatures) {
94629470
// Only elaborate errors from the first failure
94639471
let shouldElaborateErrors = reportErrors;
94649472
for (const s of sourceSignatures) {
9465-
const related = signatureRelatedTo(s, t, shouldElaborateErrors);
9473+
const related = signatureRelatedTo(s, t, /*erase*/ true, shouldElaborateErrors);
94669474
if (related) {
94679475
result &= related;
94689476
errorInfo = saveErrorInfo;
@@ -9485,8 +9493,9 @@ namespace ts {
94859493
/**
94869494
* See signatureAssignableTo, compareSignaturesIdentical
94879495
*/
9488-
function signatureRelatedTo(source: Signature, target: Signature, reportErrors: boolean): Ternary {
9489-
return compareSignaturesRelated(source, target, /*checkAsCallback*/ false, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo);
9496+
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean): Ternary {
9497+
return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
9498+
/*checkAsCallback*/ false, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo);
94909499
}
94919500

94929501
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
@@ -14915,12 +14924,15 @@ namespace ts {
1491514924
}
1491614925

1491714926
// Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec)
14918-
function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper: TypeMapper): Signature {
14927+
function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper): Signature {
1491914928
const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes);
1492014929
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
1492114930
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
14922-
inferTypes(context.inferences, instantiateType(source, contextualMapper), target);
14931+
inferTypes(context.inferences, instantiateType(source, contextualMapper || identityMapper), target);
1492314932
});
14933+
if (!contextualMapper) {
14934+
inferTypes(context.inferences, getReturnTypeOfSignature(contextualSignature), getReturnTypeOfSignature(signature), InferencePriority.ReturnType);
14935+
}
1492414936
return getSignatureInstantiation(signature, getInferredTypes(context));
1492514937
}
1492614938

0 commit comments

Comments
 (0)