Skip to content

Commit

Permalink
Cache the regularized form of union types (microsoft#37749)
Browse files Browse the repository at this point in the history
* Cache the regularized form of union types

* Inline function because why not

* Introduce two fastpasths into isRelatedTo
  • Loading branch information
weswigham committed Apr 3, 2020
1 parent 349ae45 commit 6d25c01
Show file tree
Hide file tree
Showing 6 changed files with 50,129 additions and 2 deletions.
23 changes: 21 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13489,7 +13489,7 @@ namespace ts {

function getRegularTypeOfLiteralType(type: Type): Type {
return type.flags & TypeFlags.Literal ? (<LiteralType>type).regularType :
type.flags & TypeFlags.Union ? getUnionType(sameMap((<UnionType>type).types, getRegularTypeOfLiteralType)) :
type.flags & TypeFlags.Union ? ((<UnionType>type).regularType || ((<UnionType>type).regularType = getUnionType(sameMap((<UnionType>type).types, getRegularTypeOfLiteralType)) as UnionType)) :
type;
}

Expand Down Expand Up @@ -15487,6 +15487,16 @@ namespace ts {
return isIdenticalTo(source, target);
}


// We fastpath comparing a type parameter to exactly its constraint, as this is _super_ common,
// and otherwise, for type parameters in large unions, causes us to need to compare the union to itself,
// as we break down the _target_ union first, _then_ get the source constraint - so for every
// member of the target, we attempt to find a match in the source. This avoids that in cases where
// the target is exactly the constraint.
if (source.flags & TypeFlags.TypeParameter && getConstraintOfType(source) === target) {
return Ternary.True;
}

// Try to see if we're relating something like `Foo` -> `Bar | null | undefined`.
// If so, reporting the `null` and `undefined` in the type is hardly useful.
// First, see if we're even relating an object type to a union.
Expand Down Expand Up @@ -15820,7 +15830,16 @@ namespace ts {
function eachTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
let result = Ternary.True;
const sourceTypes = source.types;
for (const sourceType of sourceTypes) {
for (let i = 0; i < sourceTypes.length; i++) {
const sourceType = sourceTypes[i];
if (target.flags & TypeFlags.Union && (target as UnionType).types.length === sourceTypes.length) {
// many unions are mappings of one another; in such cases, simply comparing members at the same index can shortcut the comparison
const related = isRelatedTo(sourceType, (target as UnionType).types[i], /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState);
if (related) {
result &= related;
continue;
}
}
const related = isRelatedTo(sourceType, target, reportErrors, /*headMessage*/ undefined, intersectionState);
if (!related) {
return Ternary.False;
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4653,6 +4653,8 @@ namespace ts {
export interface UnionType extends UnionOrIntersectionType {
/* @internal */
resolvedReducedType: Type;
/* @internal */
regularType: UnionType;
}

export interface IntersectionType extends UnionOrIntersectionType {
Expand Down
Loading

0 comments on commit 6d25c01

Please sign in to comment.