Skip to content

Commit 6d25c01

Browse files
authored
Cache the regularized form of union types (#37749)
* Cache the regularized form of union types * Inline function because why not * Introduce two fastpasths into isRelatedTo
1 parent 349ae45 commit 6d25c01

6 files changed

+50129
-2
lines changed

src/compiler/checker.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13489,7 +13489,7 @@ namespace ts {
1348913489

1349013490
function getRegularTypeOfLiteralType(type: Type): Type {
1349113491
return type.flags & TypeFlags.Literal ? (<LiteralType>type).regularType :
13492-
type.flags & TypeFlags.Union ? getUnionType(sameMap((<UnionType>type).types, getRegularTypeOfLiteralType)) :
13492+
type.flags & TypeFlags.Union ? ((<UnionType>type).regularType || ((<UnionType>type).regularType = getUnionType(sameMap((<UnionType>type).types, getRegularTypeOfLiteralType)) as UnionType)) :
1349313493
type;
1349413494
}
1349513495

@@ -15487,6 +15487,16 @@ namespace ts {
1548715487
return isIdenticalTo(source, target);
1548815488
}
1548915489

15490+
15491+
// We fastpath comparing a type parameter to exactly its constraint, as this is _super_ common,
15492+
// and otherwise, for type parameters in large unions, causes us to need to compare the union to itself,
15493+
// as we break down the _target_ union first, _then_ get the source constraint - so for every
15494+
// member of the target, we attempt to find a match in the source. This avoids that in cases where
15495+
// the target is exactly the constraint.
15496+
if (source.flags & TypeFlags.TypeParameter && getConstraintOfType(source) === target) {
15497+
return Ternary.True;
15498+
}
15499+
1549015500
// Try to see if we're relating something like `Foo` -> `Bar | null | undefined`.
1549115501
// If so, reporting the `null` and `undefined` in the type is hardly useful.
1549215502
// First, see if we're even relating an object type to a union.
@@ -15820,7 +15830,16 @@ namespace ts {
1582015830
function eachTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
1582115831
let result = Ternary.True;
1582215832
const sourceTypes = source.types;
15823-
for (const sourceType of sourceTypes) {
15833+
for (let i = 0; i < sourceTypes.length; i++) {
15834+
const sourceType = sourceTypes[i];
15835+
if (target.flags & TypeFlags.Union && (target as UnionType).types.length === sourceTypes.length) {
15836+
// many unions are mappings of one another; in such cases, simply comparing members at the same index can shortcut the comparison
15837+
const related = isRelatedTo(sourceType, (target as UnionType).types[i], /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState);
15838+
if (related) {
15839+
result &= related;
15840+
continue;
15841+
}
15842+
}
1582415843
const related = isRelatedTo(sourceType, target, reportErrors, /*headMessage*/ undefined, intersectionState);
1582515844
if (!related) {
1582615845
return Ternary.False;

src/compiler/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4653,6 +4653,8 @@ namespace ts {
46534653
export interface UnionType extends UnionOrIntersectionType {
46544654
/* @internal */
46554655
resolvedReducedType: Type;
4656+
/* @internal */
4657+
regularType: UnionType;
46564658
}
46574659

46584660
export interface IntersectionType extends UnionOrIntersectionType {

0 commit comments

Comments
 (0)