From c53294b9fdad43274a4d12b99288fbe0d69d6fac Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sat, 18 Mar 2023 21:42:50 -0700 Subject: [PATCH 01/13] Avoid calculating all properties of UnionOrIntersectionType when caller can exit early --- src/compiler/checker.ts | 66 ++++++++++++++++++++++++++++++----------- src/compiler/core.ts | 26 ++++++++++++++++ src/compiler/types.ts | 4 +++ 3 files changed, 78 insertions(+), 18 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 41ac4b6be1a17..16dd009ea5544 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -183,6 +183,7 @@ import { findAncestor, findBestPatternMatch, findIndex, + findIterator, findLast, findLastIndex, findUseStrictPrologue, @@ -948,6 +949,7 @@ import { skipTrivia, skipTypeChecking, some, + someIterator, SourceFile, SpreadAssignment, SpreadElement, @@ -13466,25 +13468,53 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] { if (!type.resolvedProperties) { - const members = createSymbolTable(); - for (const current of type.types) { - for (const prop of getPropertiesOfType(current)) { - if (!members.has(prop.escapedName)) { - const combinedProp = getPropertyOfUnionOrIntersectionType(type, prop.escapedName); - if (combinedProp) { - members.set(prop.escapedName, combinedProp); + for (const _ of iteratePropertiesOfUnionOrIntersectionType(type, /*skipYield*/ true)) { + Debug.fail("Iterator should have been empty."); + } + } + return type.resolvedProperties; + } + + function *iteratePropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType, skipYield = false) { + if (type.resolvedProperties) { + if (!skipYield) { + yield* type.resolvedProperties; + } + return; + } + + if (type.partiallyResolvedProperties) { + if (!skipYield) { + yield* type.partiallyResolvedProperties; + } + } + + const seenSymbols = type.partiallyResolvedPropertiesSeenSymbols ??= createSymbolTable(); + for (const current of type.types) { + for (const prop of getPropertiesOfType(current)) { + if (!seenSymbols.has(prop.escapedName)) { + const combinedProp = getPropertyOfUnionOrIntersectionType(type, prop.escapedName); + if (combinedProp) { + seenSymbols.set(prop.escapedName, combinedProp); + if (isNamedMember(combinedProp, prop.escapedName)) { + type.partiallyResolvedProperties = append(type.partiallyResolvedProperties, combinedProp); + if (!skipYield) { + yield combinedProp; + } } } } - // The properties of a union type are those that are present in all constituent types, so - // we only need to check the properties of the first type without index signature - if (type.flags & TypeFlags.Union && getIndexInfosOfType(current).length === 0) { - break; - } } - type.resolvedProperties = getNamedMembers(members); + // The properties of a union type are those that are present in all constituent types, so + // we only need to check the properties of the first type without index signature + if (type.flags & TypeFlags.Union && getIndexInfosOfType(current).length === 0) { + break; + } } - return type.resolvedProperties; + + type.resolvedProperties = type.partiallyResolvedProperties ?? emptyArray; + type.partiallyResolvedProperties = undefined; + type.partiallyResolvedPropertiesSeenSymbols = undefined; } function getPropertiesOfType(type: Type): Symbol[] { @@ -14136,7 +14166,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { else if (type.flags & TypeFlags.Intersection) { if (!((type as IntersectionType).objectFlags & ObjectFlags.IsNeverIntersectionComputed)) { (type as IntersectionType).objectFlags |= ObjectFlags.IsNeverIntersectionComputed | - (some(getPropertiesOfUnionOrIntersectionType(type as IntersectionType), isNeverReducedProperty) ? ObjectFlags.IsNeverIntersection : 0); + (someIterator(iteratePropertiesOfUnionOrIntersectionType(type as IntersectionType), isNeverReducedProperty) ? ObjectFlags.IsNeverIntersection : 0); } return (type as IntersectionType).objectFlags & ObjectFlags.IsNeverIntersection ? neverType : type; } @@ -14191,12 +14221,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function elaborateNeverIntersection(errorInfo: DiagnosticMessageChain | undefined, type: Type) { if (type.flags & TypeFlags.Intersection && getObjectFlags(type) & ObjectFlags.IsNeverIntersection) { - const neverProp = find(getPropertiesOfUnionOrIntersectionType(type as IntersectionType), isDiscriminantWithNeverType); + const neverProp = findIterator(iteratePropertiesOfUnionOrIntersectionType(type as IntersectionType), isDiscriminantWithNeverType); if (neverProp) { return chainDiagnosticMessages(errorInfo, Diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_has_conflicting_types_in_some_constituents, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.NoTypeReduction), symbolToString(neverProp)); } - const privateProp = find(getPropertiesOfUnionOrIntersectionType(type as IntersectionType), isConflictingPrivateProperty); + const privateProp = findIterator(iteratePropertiesOfUnionOrIntersectionType(type as IntersectionType), isConflictingPrivateProperty); if (privateProp) { return chainDiagnosticMessages(errorInfo, Diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_exists_in_multiple_constituents_and_is_private_in_some, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.NoTypeReduction), symbolToString(privateProp)); @@ -22372,7 +22402,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function membersRelatedToIndexInfo(source: Type, targetInfo: IndexInfo, reportErrors: boolean, intersectionState: IntersectionState): Ternary { let result = Ternary.True; const keyType = targetInfo.keyType; - const props = source.flags & TypeFlags.Intersection ? getPropertiesOfUnionOrIntersectionType(source as IntersectionType) : getPropertiesOfObjectType(source); + const props = source.flags & TypeFlags.Intersection ? iteratePropertiesOfUnionOrIntersectionType(source as IntersectionType) : getPropertiesOfObjectType(source); for (const prop of props) { // Skip over ignored JSX and symbol-named members if (isIgnoredJsxProperty(source, prop)) { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 1acb342daa494..45b8aa112decc 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -177,6 +177,22 @@ export function find(array: readonly T[] | undefined, predicate: (element: T, return undefined; } +/** @internal */ +export function findIterator(iter: Iterable, predicate: (element: T, index: number) => boolean, startIndex = 0): T | undefined { + let i = 0; + for (const value of iter) { + if (i < startIndex) { + i++; + continue; + } + if (predicate(value, i)) { + return value; + } + } + return undefined; +} + + /** @internal */ export function findLast(array: readonly T[] | undefined, predicate: (element: T, index: number) => element is U, startIndex?: number): U | undefined; /** @internal */ @@ -680,6 +696,16 @@ export function some(array: readonly T[] | undefined, predicate?: (value: T) return false; } +/** @internal */ +export function someIterator(iter: Iterable, predicate: (value: T) => boolean): boolean { + for (const v of iter) { + if (predicate(v)) { + return true; + } + } + return false; +} + /** * Calls the callback with (start, afterEnd) index pairs for each range where 'pred' is true. * diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 68caffea128d1..39e0948a04ad5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6422,6 +6422,10 @@ export interface UnionOrIntersectionType extends Type { resolvedStringIndexType: IndexType; /** @internal */ resolvedBaseConstraint: Type; + /** @internal */ + partiallyResolvedPropertiesSeenSymbols?: SymbolTable; + /** @internal */ + partiallyResolvedProperties?: Symbol[]; } export interface UnionType extends UnionOrIntersectionType { From 9c75a5feefdff99c6840eb5bcdd922f405926bbb Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sun, 19 Mar 2023 09:54:20 -0700 Subject: [PATCH 02/13] Make code clearer, skip even more work --- src/compiler/checker.ts | 58 +++++++++++++++++++++++------------------ src/compiler/types.ts | 6 ++--- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 16dd009ea5544..e924e1e4594d5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13472,49 +13472,55 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { Debug.fail("Iterator should have been empty."); } } - return type.resolvedProperties; + return type.resolvedProperties!; } - function *iteratePropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType, skipYield = false) { + function* iteratePropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType, skipYield = false) { if (type.resolvedProperties) { - if (!skipYield) { - yield* type.resolvedProperties; - } + Debug.assert(!skipYield); + yield* type.resolvedProperties; return; } - if (type.partiallyResolvedProperties) { + if (!skipYield && type.partiallyResolvedProperties) { + yield* type.partiallyResolvedProperties; + } + + // If we have an iterator already, pick up where we left off. + // Otherwise, we haven't started, so create a new iterator. + const generator = type.partiallyResolvedPropertiesGenerator ??= worker(); + for (const symbol of generator) { + type.partiallyResolvedProperties = append(type.partiallyResolvedProperties, symbol); if (!skipYield) { - yield* type.partiallyResolvedProperties; + yield symbol; } } - const seenSymbols = type.partiallyResolvedPropertiesSeenSymbols ??= createSymbolTable(); - for (const current of type.types) { - for (const prop of getPropertiesOfType(current)) { - if (!seenSymbols.has(prop.escapedName)) { - const combinedProp = getPropertyOfUnionOrIntersectionType(type, prop.escapedName); - if (combinedProp) { - seenSymbols.set(prop.escapedName, combinedProp); - if (isNamedMember(combinedProp, prop.escapedName)) { - type.partiallyResolvedProperties = append(type.partiallyResolvedProperties, combinedProp); - if (!skipYield) { + type.resolvedProperties = type.partiallyResolvedProperties ?? emptyArray; + type.partiallyResolvedProperties = undefined; + type.partiallyResolvedPropertiesGenerator = undefined; + + function* worker() { + const seenSymbols = createSymbolTable(); + for (const current of type.types) { + for (const prop of getPropertiesOfType(current)) { + if (!seenSymbols.has(prop.escapedName)) { + const combinedProp = getPropertyOfUnionOrIntersectionType(type, prop.escapedName); + if (combinedProp) { + seenSymbols.set(prop.escapedName, combinedProp); + if (isNamedMember(combinedProp, prop.escapedName)) { yield combinedProp; } } } } - } - // The properties of a union type are those that are present in all constituent types, so - // we only need to check the properties of the first type without index signature - if (type.flags & TypeFlags.Union && getIndexInfosOfType(current).length === 0) { - break; + // The properties of a union type are those that are present in all constituent types, so + // we only need to check the properties of the first type without index signature + if (type.flags & TypeFlags.Union && getIndexInfosOfType(current).length === 0) { + break; + } } } - - type.resolvedProperties = type.partiallyResolvedProperties ?? emptyArray; - type.partiallyResolvedProperties = undefined; - type.partiallyResolvedPropertiesSeenSymbols = undefined; } function getPropertiesOfType(type: Type): Symbol[] { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 39e0948a04ad5..b0039d834bf33 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6415,7 +6415,7 @@ export interface UnionOrIntersectionType extends Type { /** @internal */ propertyCacheWithoutObjectFunctionPropertyAugment?: SymbolTable; // Cache of resolved properties that does not augment function or object type properties /** @internal */ - resolvedProperties: Symbol[]; + resolvedProperties?: Symbol[]; /** @internal */ resolvedIndexType: IndexType; /** @internal */ @@ -6423,9 +6423,9 @@ export interface UnionOrIntersectionType extends Type { /** @internal */ resolvedBaseConstraint: Type; /** @internal */ - partiallyResolvedPropertiesSeenSymbols?: SymbolTable; - /** @internal */ partiallyResolvedProperties?: Symbol[]; + /** @internal */ + partiallyResolvedPropertiesGenerator?: Generator; } export interface UnionType extends UnionOrIntersectionType { From 8a6ba6dbe85ac4d3e9e1e196b981dc8c6790d169 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sun, 19 Mar 2023 09:55:23 -0700 Subject: [PATCH 03/13] Fix wording --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e924e1e4594d5..9f04b79ae6331 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13486,8 +13486,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { yield* type.partiallyResolvedProperties; } - // If we have an iterator already, pick up where we left off. - // Otherwise, we haven't started, so create a new iterator. + // If we have a generator already, pick up where we left off. + // Otherwise, we haven't started, so create a new one. const generator = type.partiallyResolvedPropertiesGenerator ??= worker(); for (const symbol of generator) { type.partiallyResolvedProperties = append(type.partiallyResolvedProperties, symbol); From 9c09e000441b3cc7eb25e6055cdd4d85ffa945f2 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sun, 19 Mar 2023 09:56:40 -0700 Subject: [PATCH 04/13] Flip skipYield to doYield --- src/compiler/checker.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9f04b79ae6331..40d3fa36f78f3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13468,21 +13468,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] { if (!type.resolvedProperties) { - for (const _ of iteratePropertiesOfUnionOrIntersectionType(type, /*skipYield*/ true)) { + for (const _ of iteratePropertiesOfUnionOrIntersectionType(type, /*doYield*/ false)) { Debug.fail("Iterator should have been empty."); } } return type.resolvedProperties!; } - function* iteratePropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType, skipYield = false) { + function* iteratePropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType, doYield = true) { if (type.resolvedProperties) { - Debug.assert(!skipYield); + Debug.assert(doYield); yield* type.resolvedProperties; return; } - if (!skipYield && type.partiallyResolvedProperties) { + if (doYield && type.partiallyResolvedProperties) { yield* type.partiallyResolvedProperties; } @@ -13491,7 +13491,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const generator = type.partiallyResolvedPropertiesGenerator ??= worker(); for (const symbol of generator) { type.partiallyResolvedProperties = append(type.partiallyResolvedProperties, symbol); - if (!skipYield) { + if (doYield) { yield symbol; } } From 2a484922aa99e954dbcaf03d03011888228a5060 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sun, 19 Mar 2023 09:58:42 -0700 Subject: [PATCH 05/13] While I'm here, remove some lies --- src/compiler/types.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b0039d834bf33..4e3359ed10e64 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6417,11 +6417,11 @@ export interface UnionOrIntersectionType extends Type { /** @internal */ resolvedProperties?: Symbol[]; /** @internal */ - resolvedIndexType: IndexType; + resolvedIndexType?: IndexType; /** @internal */ - resolvedStringIndexType: IndexType; + resolvedStringIndexType?: IndexType; /** @internal */ - resolvedBaseConstraint: Type; + resolvedBaseConstraint?: Type; /** @internal */ partiallyResolvedProperties?: Symbol[]; /** @internal */ From 7c97848a22cecfc558068557238383478a0340a2 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sun, 19 Mar 2023 10:07:23 -0700 Subject: [PATCH 06/13] Fix findIterator --- src/compiler/core.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 45b8aa112decc..63ec506464341 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -181,13 +181,12 @@ export function find(array: readonly T[] | undefined, predicate: (element: T, export function findIterator(iter: Iterable, predicate: (element: T, index: number) => boolean, startIndex = 0): T | undefined { let i = 0; for (const value of iter) { - if (i < startIndex) { - i++; - continue; - } - if (predicate(value, i)) { - return value; + if (i >= startIndex) { + if (predicate(value, i)) { + return value; + } } + i++; } return undefined; } From b8d36647f2166c32f16fd8d0730e04db2f29a947 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sun, 19 Mar 2023 10:10:21 -0700 Subject: [PATCH 07/13] Use a Set --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dcc9189906b44..873492cf3119f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13501,13 +13501,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { type.partiallyResolvedPropertiesGenerator = undefined; function* worker() { - const seenSymbols = createSymbolTable(); + const seenSymbols = new Set<__String>(); for (const current of type.types) { for (const prop of getPropertiesOfType(current)) { if (!seenSymbols.has(prop.escapedName)) { const combinedProp = getPropertyOfUnionOrIntersectionType(type, prop.escapedName); if (combinedProp) { - seenSymbols.set(prop.escapedName, combinedProp); + seenSymbols.add(prop.escapedName); if (isNamedMember(combinedProp, prop.escapedName)) { yield combinedProp; } From 21ccbb928af8e69fab48d04b5c164657231a4c31 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sun, 19 Mar 2023 10:54:19 -0700 Subject: [PATCH 08/13] Move out into worker, which seems to be faster --- src/compiler/checker.ts | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 873492cf3119f..1e06643a6e958 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13488,7 +13488,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // If we have a generator already, pick up where we left off. // Otherwise, we haven't started, so create a new one. - const generator = type.partiallyResolvedPropertiesGenerator ??= worker(); + const generator = type.partiallyResolvedPropertiesGenerator ??= iteratePropertiesOfUnionOrIntersectionTypeWorker(type); for (const symbol of generator) { type.partiallyResolvedProperties = append(type.partiallyResolvedProperties, symbol); if (doYield) { @@ -13499,26 +13499,26 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { type.resolvedProperties = type.partiallyResolvedProperties ?? emptyArray; type.partiallyResolvedProperties = undefined; type.partiallyResolvedPropertiesGenerator = undefined; + } - function* worker() { - const seenSymbols = new Set<__String>(); - for (const current of type.types) { - for (const prop of getPropertiesOfType(current)) { - if (!seenSymbols.has(prop.escapedName)) { - const combinedProp = getPropertyOfUnionOrIntersectionType(type, prop.escapedName); - if (combinedProp) { - seenSymbols.add(prop.escapedName); - if (isNamedMember(combinedProp, prop.escapedName)) { - yield combinedProp; - } + function* iteratePropertiesOfUnionOrIntersectionTypeWorker(type: UnionOrIntersectionType) { + const seenSymbols = new Set<__String>(); + for (const current of type.types) { + for (const prop of getPropertiesOfType(current)) { + if (!seenSymbols.has(prop.escapedName)) { + const combinedProp = getPropertyOfUnionOrIntersectionType(type, prop.escapedName); + if (combinedProp) { + seenSymbols.add(prop.escapedName); + if (isNamedMember(combinedProp, prop.escapedName)) { + yield combinedProp; } } } - // The properties of a union type are those that are present in all constituent types, so - // we only need to check the properties of the first type without index signature - if (type.flags & TypeFlags.Union && getIndexInfosOfType(current).length === 0) { - break; - } + } + // The properties of a union type are those that are present in all constituent types, so + // we only need to check the properties of the first type without index signature + if (type.flags & TypeFlags.Union && getIndexInfosOfType(current).length === 0) { + break; } } } From ae6df30eae0ff61c27b2a80b9e002b15d07b39cb Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sun, 19 Mar 2023 19:23:06 -0700 Subject: [PATCH 09/13] Clearer semantics --- src/compiler/checker.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1e06643a6e958..ffc719696dd3f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13467,18 +13467,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] { - if (!type.resolvedProperties) { - for (const _ of iteratePropertiesOfUnionOrIntersectionType(type, /*doYield*/ false)) { - Debug.fail("Iterator should have been empty."); - } + for (const _ of iteratePropertiesOfUnionOrIntersectionType(type, /*doYield*/ false)) { + Debug.fail("Iterator should have been empty."); } return type.resolvedProperties!; } function* iteratePropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType, doYield = true) { if (type.resolvedProperties) { - Debug.assert(doYield); - yield* type.resolvedProperties; + if (doYield) { + yield* type.resolvedProperties; + } return; } From e6be1d47f5a307af0a480142f4e4cd1326799e63 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sun, 19 Mar 2023 19:29:47 -0700 Subject: [PATCH 10/13] It's just getting more and more complicated --- src/compiler/checker.ts | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ffc719696dd3f..88b57af6f0e42 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1241,6 +1241,7 @@ const enum TypeSystemPropertyName { ResolvedBaseTypes, WriteType, ParameterInitializerContainsUndefined, + ResolvedProperties, } /** @internal */ @@ -10129,6 +10130,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return !!getSymbolLinks(target as Symbol).writeType; case TypeSystemPropertyName.ParameterInitializerContainsUndefined: return getNodeLinks(target as ParameterDeclaration).parameterInitializerContainsUndefined !== undefined; + case TypeSystemPropertyName.ResolvedProperties: + return !!(target as UnionOrIntersectionType).resolvedProperties; } return Debug.assertNever(propertyName); } @@ -13485,13 +13488,26 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { yield* type.partiallyResolvedProperties; } + if (!pushTypeResolution(type, TypeSystemPropertyName.ResolvedProperties)) { + return; + } + // If we have a generator already, pick up where we left off. // Otherwise, we haven't started, so create a new one. - const generator = type.partiallyResolvedPropertiesGenerator ??= iteratePropertiesOfUnionOrIntersectionTypeWorker(type); - for (const symbol of generator) { - type.partiallyResolvedProperties = append(type.partiallyResolvedProperties, symbol); - if (doYield) { - yield symbol; + try { + const generator = type.partiallyResolvedPropertiesGenerator ??= iteratePropertiesOfUnionOrIntersectionTypeWorker(type); + for (const symbol of generator) { + type.partiallyResolvedProperties = append(type.partiallyResolvedProperties, symbol); + if (doYield) { + yield symbol; + } + } + } + finally { + // Ensure we pop when we're all done iterating. If done outside of a finally, + // this code won't actually execute. + if (!popTypeResolution()) { + return; } } From 36617b9c89fe76ff1cd28e2a7d67babb4b060c1e Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sun, 19 Mar 2023 19:35:52 -0700 Subject: [PATCH 11/13] Fix lint, for now --- src/compiler/checker.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 88b57af6f0e42..52371aa3534ec 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13506,9 +13506,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { finally { // Ensure we pop when we're all done iterating. If done outside of a finally, // this code won't actually execute. - if (!popTypeResolution()) { - return; - } + popTypeResolution(); } type.resolvedProperties = type.partiallyResolvedProperties ?? emptyArray; From 391f090459d916f68f3413be92bdbc426fb062f5 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 22 Aug 2023 13:51:30 -0700 Subject: [PATCH 12/13] dprint --- src/compiler/core.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 2fd919452ac87..a9f7372c55e56 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -190,7 +190,6 @@ export function findIterator(iter: Iterable, predicate: (element: T, index return undefined; } - /** @internal */ export function findLast(array: readonly T[] | undefined, predicate: (element: T, index: number) => element is U, startIndex?: number): U | undefined; /** @internal */ From 6148cdec58674fd6dfe473edf8463d16d0797ba5 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 24 Aug 2023 10:23:09 -0700 Subject: [PATCH 13/13] Small tweak to make default clearer --- src/compiler/checker.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 20fc0c5ec8899..b63c401ca49a4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13780,21 +13780,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] { - for (const _ of iteratePropertiesOfUnionOrIntersectionType(type, /*doYield*/ false)) { + for (const _ of iteratePropertiesOfUnionOrIntersectionType(type, /*skipYield*/ true)) { Debug.fail("Iterator should have been empty."); } return type.resolvedProperties!; } - function* iteratePropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType, doYield = true) { + function* iteratePropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType, skipYield = false) { if (type.resolvedProperties) { - if (doYield) { + if (!skipYield) { yield* type.resolvedProperties; } return; } - if (doYield && type.partiallyResolvedProperties) { + if (!skipYield && type.partiallyResolvedProperties) { yield* type.partiallyResolvedProperties; } @@ -13808,7 +13808,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const generator = type.partiallyResolvedPropertiesGenerator ??= iteratePropertiesOfUnionOrIntersectionTypeWorker(type); for (const symbol of generator) { type.partiallyResolvedProperties = append(type.partiallyResolvedProperties, symbol); - if (doYield) { + if (!skipYield) { yield symbol; } }