Skip to content

Commit 9c6f651

Browse files
committed
Refactor truthy-spread-union creation for performance
Only create properties once, only if needed, and don't create an intermediate anonymous type. The code is also inlined with the rest of `getSpreadType`.
1 parent 18653a5 commit 9c6f651

1 file changed

Lines changed: 22 additions & 38 deletions

File tree

src/compiler/checker.ts

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7822,6 +7822,7 @@ namespace ts {
78227822
* and right = the new element to be spread.
78237823
*/
78247824
function getSpreadType(left: Type, right: Type): Type {
7825+
let truthyRight: Type;
78257826
if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) {
78267827
return anyType;
78277828
}
@@ -7832,25 +7833,18 @@ namespace ts {
78327833
return left;
78337834
}
78347835
if (left.flags & TypeFlags.Union) {
7835-
// if the union is `false | T` make all the properties of T optional
7836-
const wl = getPartialTypeFromFalsyUnion(left as UnionType);
7837-
if (wl) {
7838-
left = wl;
7839-
}
7840-
else {
7841-
return mapType(left, t => getSpreadType(t, right));
7842-
}
7836+
return mapType(left, t => getSpreadType(t, right));
78437837
}
78447838
if (right.flags & TypeFlags.Union) {
7845-
const wr = getPartialTypeFromFalsyUnion(right as UnionType);
7846-
if (wr) {
7847-
right = wr;
7839+
truthyRight = getTruthyTypeFromFalsyUnion(right as UnionType);
7840+
if (!truthyRight || truthyRight.flags & TypeFlags.Union) {
7841+
return mapType(right, t => getSpreadType(left, t));
78487842
}
78497843
else {
7850-
return mapType(right, t => getSpreadType(left, t));
7844+
right = truthyRight;
78517845
}
78527846
}
7853-
if (right.flags & (TypeFlags.NonPrimitive | TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.StringLike)) {
7847+
if (right.flags & (TypeFlags.NonPrimitive | TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.EnumLike)) {
78547848
return emptyObjectType;
78557849
}
78567850

@@ -7875,9 +7869,10 @@ namespace ts {
78757869
skippedPrivateMembers.set(rightProp.escapedName, true);
78767870
}
78777871
else if (!isClassMethod(rightProp) && !isSetterWithoutGetter) {
7878-
members.set(rightProp.escapedName, getNonReadonlySymbol(rightProp));
7872+
members.set(rightProp.escapedName, getSymbolOfSpreadProperty(rightProp, !!truthyRight));
78797873
}
78807874
}
7875+
78817876
for (const leftProp of getPropertiesOfType(left)) {
78827877
if (leftProp.flags & SymbolFlags.SetAccessor && !(leftProp.flags & SymbolFlags.GetAccessor)
78837878
|| skippedPrivateMembers.has(leftProp.escapedName)
@@ -7899,19 +7894,22 @@ namespace ts {
78997894
}
79007895
}
79017896
else {
7902-
members.set(leftProp.escapedName, getNonReadonlySymbol(leftProp));
7897+
members.set(leftProp.escapedName, getSymbolOfSpreadProperty(leftProp, /*makeOptional*/ false));
79037898
}
79047899
}
79057900
return createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
79067901
}
79077902

7908-
function getNonReadonlySymbol(prop: Symbol) {
7909-
if (!isReadonlySymbol(prop)) {
7903+
function getSymbolOfSpreadProperty(prop: Symbol, makeOptional: boolean) {
7904+
if (!isReadonlySymbol(prop) && (!makeOptional || prop.flags & SymbolFlags.Optional)) {
79107905
return prop;
79117906
}
7912-
const flags = SymbolFlags.Property | (prop.flags & SymbolFlags.Optional);
7907+
const flags = SymbolFlags.Property | (makeOptional ? SymbolFlags.Optional : prop.flags & SymbolFlags.Optional);
79137908
const result = createSymbol(flags, prop.escapedName);
79147909
result.type = getTypeOfSymbol(prop);
7910+
if (makeOptional) {
7911+
result.type = getUnionType([result.type, undefinedType]);
7912+
}
79157913
result.declarations = prop.declarations;
79167914
result.syntheticOrigin = prop;
79177915
return result;
@@ -7921,25 +7919,10 @@ namespace ts {
79217919
return prop.flags & SymbolFlags.Method && find(prop.declarations, decl => isClassLike(decl.parent));
79227920
}
79237921

7924-
function getPartialTypeFromFalsyUnion(type: UnionType): Type | undefined {
7925-
if (type.types.length === 2) {
7926-
const truthy = removeDefinitelyFalsyTypes(type);
7927-
if (truthy !== type) {
7928-
const members = createSymbolTable();
7929-
for (const prop of getPropertiesOfType(truthy)) {
7930-
if (prop.flags & SymbolFlags.Optional) {
7931-
members.set(prop.escapedName, prop);
7932-
}
7933-
else {
7934-
const result = createSymbol(prop.flags | SymbolFlags.Optional, prop.escapedName);
7935-
result.type = getUnionType([getTypeOfSymbol(prop), undefinedType]);
7936-
result.declarations = prop.declarations;
7937-
result.syntheticOrigin = prop;
7938-
members.set(prop.escapedName, result);
7939-
}
7940-
}
7941-
return createAnonymousType(undefined, members, emptyArray, emptyArray, getIndexInfoOfType(truthy, IndexKind.String), getIndexInfoOfType(truthy, IndexKind.Number));
7942-
}
7922+
function getTruthyTypeFromFalsyUnion(type: UnionType): Type | undefined {
7923+
const truthy = removeDefinitelyFalsyTypes(type);
7924+
if (truthy !== type) {
7925+
return truthy;
79437926
}
79447927
}
79457928

@@ -13784,7 +13767,8 @@ namespace ts {
1378413767
}
1378513768

1378613769
function isValidSpreadType(type: Type): boolean {
13787-
return !!(type.flags & (TypeFlags.Any | TypeFlags.PossiblyFalsy | TypeFlags.NonPrimitive) ||
13770+
return !!(type.flags & (TypeFlags.Any | TypeFlags.NonPrimitive) ||
13771+
getFalsyFlags(type) & TypeFlags.DefinitelyFalsy && isValidSpreadType(removeDefinitelyFalsyTypes(type)) ||
1378813772
type.flags & TypeFlags.Object && !isGenericMappedType(type) ||
1378913773
type.flags & TypeFlags.UnionOrIntersection && !forEach((<UnionOrIntersectionType>type).types, t => !isValidSpreadType(t)));
1379013774
}

0 commit comments

Comments
 (0)