Skip to content

Commit afdd9b5

Browse files
authored
Merge pull request #18279 from Microsoft/canonicalSignatures
Optimize strict generic signature checking performance
2 parents d790f1d + fc16330 commit afdd9b5

File tree

2 files changed

+28
-6
lines changed

2 files changed

+28
-6
lines changed

src/compiler/checker.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6632,11 +6632,30 @@ namespace ts {
66326632
}
66336633

66346634
function getErasedSignature(signature: Signature): Signature {
6635-
if (!signature.typeParameters) return signature;
6636-
if (!signature.erasedSignatureCache) {
6637-
signature.erasedSignatureCache = instantiateSignature(signature, createTypeEraser(signature.typeParameters), /*eraseTypeParameters*/ true);
6638-
}
6639-
return signature.erasedSignatureCache;
6635+
return signature.typeParameters ?
6636+
signature.erasedSignatureCache || (signature.erasedSignatureCache = createErasedSignature(signature)) :
6637+
signature;
6638+
}
6639+
6640+
function createErasedSignature(signature: Signature) {
6641+
// Create an instantiation of the signature where all type arguments are the any type.
6642+
return instantiateSignature(signature, createTypeEraser(signature.typeParameters), /*eraseTypeParameters*/ true);
6643+
}
6644+
6645+
function getCanonicalSignature(signature: Signature): Signature {
6646+
return signature.typeParameters ?
6647+
signature.canonicalSignatureCache || (signature.canonicalSignatureCache = createCanonicalSignature(signature)) :
6648+
signature;
6649+
}
6650+
6651+
function createCanonicalSignature(signature: Signature) {
6652+
// Create an instantiation of the signature where each unconstrained type parameter is replaced with
6653+
// its original. When a generic class or interface is instantiated, each generic method in the class or
6654+
// interface is instantiated with a fresh set of cloned type parameters (which we need to handle scenarios
6655+
// where different generations of the same type parameter are in scope). This leads to a lot of new type
6656+
// identities, and potentially a lot of work comparing those identities, so here we create an instantiation
6657+
// that uses the original type identities for all unconstrained type parameters.
6658+
return getSignatureInstantiation(signature, map(signature.typeParameters, tp => tp.target && !getConstraintOfTypeParameter(tp.target) ? tp.target : tp));
66406659
}
66416660

66426661
function getOrCreateTypeFromSignature(signature: Signature): ObjectType {
@@ -8473,7 +8492,8 @@ namespace ts {
84738492
return Ternary.False;
84748493
}
84758494

8476-
if (source.typeParameters) {
8495+
if (source.typeParameters && source.typeParameters !== target.typeParameters) {
8496+
target = getCanonicalSignature(target);
84778497
source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes);
84788498
}
84798499

src/compiler/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3447,6 +3447,8 @@ namespace ts {
34473447
/* @internal */
34483448
erasedSignatureCache?: Signature; // Erased version of signature (deferred)
34493449
/* @internal */
3450+
canonicalSignatureCache?: Signature; // Canonical version of signature (deferred)
3451+
/* @internal */
34503452
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
34513453
/* @internal */
34523454
typePredicate?: TypePredicate;

0 commit comments

Comments
 (0)