Skip to content

Commit c53294b

Browse files
committed
Avoid calculating all properties of UnionOrIntersectionType when caller can exit early
1 parent 905a0b4 commit c53294b

3 files changed

Lines changed: 78 additions & 18 deletions

File tree

‎src/compiler/checker.ts‎

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ import {
183183
findAncestor,
184184
findBestPatternMatch,
185185
findIndex,
186+
findIterator,
186187
findLast,
187188
findLastIndex,
188189
findUseStrictPrologue,
@@ -948,6 +949,7 @@ import {
948949
skipTrivia,
949950
skipTypeChecking,
950951
some,
952+
someIterator,
951953
SourceFile,
952954
SpreadAssignment,
953955
SpreadElement,
@@ -13466,25 +13468,53 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1346613468

1346713469
function getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] {
1346813470
if (!type.resolvedProperties) {
13469-
const members = createSymbolTable();
13470-
for (const current of type.types) {
13471-
for (const prop of getPropertiesOfType(current)) {
13472-
if (!members.has(prop.escapedName)) {
13473-
const combinedProp = getPropertyOfUnionOrIntersectionType(type, prop.escapedName);
13474-
if (combinedProp) {
13475-
members.set(prop.escapedName, combinedProp);
13471+
for (const _ of iteratePropertiesOfUnionOrIntersectionType(type, /*skipYield*/ true)) {
13472+
Debug.fail("Iterator should have been empty.");
13473+
}
13474+
}
13475+
return type.resolvedProperties;
13476+
}
13477+
13478+
function *iteratePropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType, skipYield = false) {
13479+
if (type.resolvedProperties) {
13480+
if (!skipYield) {
13481+
yield* type.resolvedProperties;
13482+
}
13483+
return;
13484+
}
13485+
13486+
if (type.partiallyResolvedProperties) {
13487+
if (!skipYield) {
13488+
yield* type.partiallyResolvedProperties;
13489+
}
13490+
}
13491+
13492+
const seenSymbols = type.partiallyResolvedPropertiesSeenSymbols ??= createSymbolTable();
13493+
for (const current of type.types) {
13494+
for (const prop of getPropertiesOfType(current)) {
13495+
if (!seenSymbols.has(prop.escapedName)) {
13496+
const combinedProp = getPropertyOfUnionOrIntersectionType(type, prop.escapedName);
13497+
if (combinedProp) {
13498+
seenSymbols.set(prop.escapedName, combinedProp);
13499+
if (isNamedMember(combinedProp, prop.escapedName)) {
13500+
type.partiallyResolvedProperties = append(type.partiallyResolvedProperties, combinedProp);
13501+
if (!skipYield) {
13502+
yield combinedProp;
13503+
}
1347613504
}
1347713505
}
1347813506
}
13479-
// The properties of a union type are those that are present in all constituent types, so
13480-
// we only need to check the properties of the first type without index signature
13481-
if (type.flags & TypeFlags.Union && getIndexInfosOfType(current).length === 0) {
13482-
break;
13483-
}
1348413507
}
13485-
type.resolvedProperties = getNamedMembers(members);
13508+
// The properties of a union type are those that are present in all constituent types, so
13509+
// we only need to check the properties of the first type without index signature
13510+
if (type.flags & TypeFlags.Union && getIndexInfosOfType(current).length === 0) {
13511+
break;
13512+
}
1348613513
}
13487-
return type.resolvedProperties;
13514+
13515+
type.resolvedProperties = type.partiallyResolvedProperties ?? emptyArray;
13516+
type.partiallyResolvedProperties = undefined;
13517+
type.partiallyResolvedPropertiesSeenSymbols = undefined;
1348813518
}
1348913519

1349013520
function getPropertiesOfType(type: Type): Symbol[] {
@@ -14136,7 +14166,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1413614166
else if (type.flags & TypeFlags.Intersection) {
1413714167
if (!((type as IntersectionType).objectFlags & ObjectFlags.IsNeverIntersectionComputed)) {
1413814168
(type as IntersectionType).objectFlags |= ObjectFlags.IsNeverIntersectionComputed |
14139-
(some(getPropertiesOfUnionOrIntersectionType(type as IntersectionType), isNeverReducedProperty) ? ObjectFlags.IsNeverIntersection : 0);
14169+
(someIterator(iteratePropertiesOfUnionOrIntersectionType(type as IntersectionType), isNeverReducedProperty) ? ObjectFlags.IsNeverIntersection : 0);
1414014170
}
1414114171
return (type as IntersectionType).objectFlags & ObjectFlags.IsNeverIntersection ? neverType : type;
1414214172
}
@@ -14191,12 +14221,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1419114221

1419214222
function elaborateNeverIntersection(errorInfo: DiagnosticMessageChain | undefined, type: Type) {
1419314223
if (type.flags & TypeFlags.Intersection && getObjectFlags(type) & ObjectFlags.IsNeverIntersection) {
14194-
const neverProp = find(getPropertiesOfUnionOrIntersectionType(type as IntersectionType), isDiscriminantWithNeverType);
14224+
const neverProp = findIterator(iteratePropertiesOfUnionOrIntersectionType(type as IntersectionType), isDiscriminantWithNeverType);
1419514225
if (neverProp) {
1419614226
return chainDiagnosticMessages(errorInfo, Diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_has_conflicting_types_in_some_constituents,
1419714227
typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.NoTypeReduction), symbolToString(neverProp));
1419814228
}
14199-
const privateProp = find(getPropertiesOfUnionOrIntersectionType(type as IntersectionType), isConflictingPrivateProperty);
14229+
const privateProp = findIterator(iteratePropertiesOfUnionOrIntersectionType(type as IntersectionType), isConflictingPrivateProperty);
1420014230
if (privateProp) {
1420114231
return chainDiagnosticMessages(errorInfo, Diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_exists_in_multiple_constituents_and_is_private_in_some,
1420214232
typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.NoTypeReduction), symbolToString(privateProp));
@@ -22372,7 +22402,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2237222402
function membersRelatedToIndexInfo(source: Type, targetInfo: IndexInfo, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
2237322403
let result = Ternary.True;
2237422404
const keyType = targetInfo.keyType;
22375-
const props = source.flags & TypeFlags.Intersection ? getPropertiesOfUnionOrIntersectionType(source as IntersectionType) : getPropertiesOfObjectType(source);
22405+
const props = source.flags & TypeFlags.Intersection ? iteratePropertiesOfUnionOrIntersectionType(source as IntersectionType) : getPropertiesOfObjectType(source);
2237622406
for (const prop of props) {
2237722407
// Skip over ignored JSX and symbol-named members
2237822408
if (isIgnoredJsxProperty(source, prop)) {

‎src/compiler/core.ts‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,22 @@ export function find<T>(array: readonly T[] | undefined, predicate: (element: T,
177177
return undefined;
178178
}
179179

180+
/** @internal */
181+
export function findIterator<T>(iter: Iterable<T>, predicate: (element: T, index: number) => boolean, startIndex = 0): T | undefined {
182+
let i = 0;
183+
for (const value of iter) {
184+
if (i < startIndex) {
185+
i++;
186+
continue;
187+
}
188+
if (predicate(value, i)) {
189+
return value;
190+
}
191+
}
192+
return undefined;
193+
}
194+
195+
180196
/** @internal */
181197
export function findLast<T, U extends T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => element is U, startIndex?: number): U | undefined;
182198
/** @internal */
@@ -680,6 +696,16 @@ export function some<T>(array: readonly T[] | undefined, predicate?: (value: T)
680696
return false;
681697
}
682698

699+
/** @internal */
700+
export function someIterator<T>(iter: Iterable<T>, predicate: (value: T) => boolean): boolean {
701+
for (const v of iter) {
702+
if (predicate(v)) {
703+
return true;
704+
}
705+
}
706+
return false;
707+
}
708+
683709
/**
684710
* Calls the callback with (start, afterEnd) index pairs for each range where 'pred' is true.
685711
*

‎src/compiler/types.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6422,6 +6422,10 @@ export interface UnionOrIntersectionType extends Type {
64226422
resolvedStringIndexType: IndexType;
64236423
/** @internal */
64246424
resolvedBaseConstraint: Type;
6425+
/** @internal */
6426+
partiallyResolvedPropertiesSeenSymbols?: SymbolTable;
6427+
/** @internal */
6428+
partiallyResolvedProperties?: Symbol[];
64256429
}
64266430

64276431
export interface UnionType extends UnionOrIntersectionType {

0 commit comments

Comments
 (0)