@@ -4553,17 +4553,17 @@ namespace ts {
45534553 ? symbolToTypeNode(type.symbol, context, SymbolFlags.Type)
45544554 : factory.createTypeReferenceNode(factory.createIdentifier("?"), /*typeArguments*/ undefined);
45554555 }
4556+ if (type.flags & TypeFlags.Union && (<UnionType>type).origin) {
4557+ type = (<UnionType>type).origin!;
4558+ }
45564559 if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) {
4557- const isIntersection = type.flags & TypeFlags.Intersection || (<UnionType>type).origin?.isIntersection;
4558- const types = type.flags & TypeFlags.Intersection ? (<IntersectionType>type).types :
4559- isIntersection ? (<UnionType>type).origin!.types :
4560- formatUnionTypes((<UnionType>type).origin?.types || (<UnionType>type).types);
4560+ const types = type.flags & TypeFlags.Union ? formatUnionTypes((<UnionType>type).types) : (<IntersectionType>type).types;
45614561 if (length(types) === 1) {
45624562 return typeToTypeNodeHelper(types[0], context);
45634563 }
45644564 const typeNodes = mapToTypeNodes(types, context, /*isBareList*/ true);
45654565 if (typeNodes && typeNodes.length > 0) {
4566- return isIntersection ? factory.createIntersectionTypeNode (typeNodes) : factory.createUnionTypeNode (typeNodes);
4566+ return type.flags & TypeFlags.Union ? factory.createUnionTypeNode (typeNodes) : factory.createIntersectionTypeNode (typeNodes);
45674567 }
45684568 else {
45694569 if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowEmptyUnionOrIntersection)) {
@@ -13256,7 +13256,7 @@ namespace ts {
1325613256 // expression constructs such as array literals and the || and ?: operators). Named types can
1325713257 // circularly reference themselves and therefore cannot be subtype reduced during their declaration.
1325813258 // For example, "type Item = string | (() => Item" is a named type that circularly references itself.
13259- function getUnionType(types: readonly Type[], unionReduction: UnionReduction = UnionReduction.Literal, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], origin?: UnionOrigin ): Type {
13259+ function getUnionType(types: readonly Type[], unionReduction: UnionReduction = UnionReduction.Literal, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], origin?: Type ): Type {
1326013260 if (types.length === 0) {
1326113261 return neverType;
1326213262 }
@@ -13298,14 +13298,17 @@ namespace ts {
1329813298 reducedTypes.push(t);
1329913299 }
1330013300 }
13301- for (const t of namedUnions) {
13302- insertType(reducedTypes, t);
13303- }
13304- if (!aliasSymbol && reducedTypes.length === 1) {
13305- return reducedTypes[0];
13301+ if (!aliasSymbol && namedUnions.length === 1 && reducedTypes.length === 0) {
13302+ return namedUnions[0];
1330613303 }
13307- if (reducedTypes.length <= typeSet.length) {
13308- origin = { types: reducedTypes, isIntersection: false };
13304+ // We create a denormalized origin type only when the union was created from one or more named unions
13305+ // (unions with alias symbols or origins) and when there is no overlap between those named unions.
13306+ const namedTypesCount = reduceLeft(namedUnions, (sum, union) => sum + (<UnionType>union).types.length, 0);
13307+ if (namedTypesCount + reducedTypes.length === typeSet.length) {
13308+ for (const t of namedUnions) {
13309+ insertType(reducedTypes, t);
13310+ }
13311+ origin = createUnionType(reducedTypes);
1330913312 }
1331013313 }
1331113314 const objectFlags = (includes & TypeFlags.NotPrimitiveUnion ? 0 : ObjectFlags.PrimitiveUnion) |
@@ -13345,24 +13348,34 @@ namespace ts {
1334513348 return a.kind === b.kind && a.parameterIndex === b.parameterIndex;
1334613349 }
1334713350
13351+ function createUnionType(types: Type[], aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], origin?: Type) {
13352+ const result = <UnionType>createType(TypeFlags.Union);
13353+ result.objectFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable);
13354+ result.types = types;
13355+ result.origin = origin;
13356+ result.aliasSymbol = aliasSymbol;
13357+ result.aliasTypeArguments = aliasTypeArguments;
13358+ return result;
13359+ }
13360+
1334813361 // This function assumes the constituent type list is sorted and deduplicated.
13349- function getUnionTypeFromSortedList(types: Type[], objectFlags: ObjectFlags, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], origin?: UnionOrigin ): Type {
13362+ function getUnionTypeFromSortedList(types: Type[], objectFlags: ObjectFlags, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], origin?: Type ): Type {
1335013363 if (types.length === 0) {
1335113364 return neverType;
1335213365 }
1335313366 if (types.length === 1) {
1335413367 return types[0];
1335513368 }
13356- const id = (origin ? `${origin.isIntersection ? "&" : "|"}${getTypeListId(origin.types)}` : getTypeListId(types)) + (aliasSymbol ? `@${getSymbolId(aliasSymbol)}` : "");
13369+ const typeKey = !origin ? getTypeListId(types) :
13370+ origin.flags & TypeFlags.Union ? `|${getTypeListId((<UnionType>origin).types)}` :
13371+ origin.flags & TypeFlags.Intersection ? `&${getTypeListId((<IntersectionType>origin).types)}` :
13372+ `#${(<IndexType>origin).type.id}`;
13373+ const id = typeKey + (aliasSymbol ? `@${getSymbolId(aliasSymbol)}` : "");
1335713374 let type = unionTypes.get(id);
1335813375 if (!type) {
13359- type = <UnionType>createType(TypeFlags.Union);
13376+ type = createUnionType(types, aliasSymbol, aliasTypeArguments, origin);
13377+ type.objectFlags |= objectFlags;
1336013378 unionTypes.set(id, type);
13361- type.objectFlags = objectFlags | getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable);
13362- type.types = types;
13363- type.origin = origin;
13364- type.aliasSymbol = aliasSymbol;
13365- type.aliasTypeArguments = aliasTypeArguments;
1336613379 }
1336713380 return type;
1336813381 }
@@ -13611,14 +13624,16 @@ namespace ts {
1361113624 result = getUnionType([getIntersectionType(typeSet), nullType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments);
1361213625 }
1361313626 else {
13614- // We are attempting to construct a type of the form X & (A | B) & Y . Transform this into a type of
13615- // the form X & A & Y | X & B & Y and recursively reduce until no union type constituents remain.
13616- // If the estimated size of the resulting union type exceeds 100000 constituents, report an error.
13627+ // We are attempting to construct a type of the form X & (A | B) & (C | D) . Transform this into a type of
13628+ // the form X & A & C | X & A & D | X & B & C | X & B & D. If the estimated size of the resulting union type
13629+ // exceeds 100000 constituents, report an error.
1361713630 if (!checkCrossProductUnion(typeSet)) {
1361813631 return errorType;
1361913632 }
1362013633 const constituents = getCrossProductIntersections(typeSet);
13621- const origin = some(constituents, t => !!(t.flags & TypeFlags.Intersection)) ? { types: typeSet, isIntersection: true } : undefined;
13634+ // We attach a denormalized origin type when at least one constituent of the cross-product union is an
13635+ // intersection (i.e. when the intersection didn't just reduce one or more unions to smaller unions).
13636+ const origin = some(constituents, t => !!(t.flags & TypeFlags.Intersection)) ? createIntersectionType(typeSet) : undefined;
1362213637 result = getUnionType(constituents, UnionReduction.Literal, aliasSymbol, aliasTypeArguments, origin);
1362313638 }
1362413639 }
@@ -13630,8 +13645,12 @@ namespace ts {
1363013645 return result;
1363113646 }
1363213647
13648+ function getCrossProductUnionSize(types: readonly Type[]) {
13649+ return reduceLeft(types, (n, t) => t.flags & TypeFlags.Union ? n * (<UnionType>t).types.length : t.flags & TypeFlags.Never ? 0 : n, 1);
13650+ }
13651+
1363313652 function checkCrossProductUnion(types: readonly Type[]) {
13634- const size = reduceLeft (types, (n, t) => n * (t.flags & TypeFlags.Union ? (<UnionType>t).types.length : t.flags & TypeFlags.Never ? 0 : 1), 1 );
13653+ const size = getCrossProductUnionSize (types);
1363513654 if (size >= 100000) {
1363613655 tracing.instant(tracing.Phase.CheckTypes, "checkCrossProductUnion_DepthLimit", { typeIds: types.map(t => t.id), size });
1363713656 error(currentNode, Diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent);
@@ -13640,17 +13659,17 @@ namespace ts {
1364013659 return true;
1364113660 }
1364213661
13643- function getCrossProductIntersections(typeSet : readonly Type[]) {
13644- const count = reduceLeft(typeSet, (n, t) => t.flags & TypeFlags.Union ? n * (<UnionType>t). types.length : n, 1 );
13662+ function getCrossProductIntersections(types : readonly Type[]) {
13663+ const count = getCrossProductUnionSize( types);
1364513664 const intersections: Type[] = [];
1364613665 for (let i = 0; i < count; i++) {
13647- const constituents = typeSet .slice();
13666+ const constituents = types .slice();
1364813667 let n = i;
13649- for (let j = typeSet .length - 1; j >= 0; j--) {
13650- if (typeSet [j].flags & TypeFlags.Union) {
13651- const types = (<UnionType>typeSet [j]).types;
13652- const length = types .length;
13653- constituents[j] = types [n % length];
13668+ for (let j = types .length - 1; j >= 0; j--) {
13669+ if (types [j].flags & TypeFlags.Union) {
13670+ const sourceTypes = (<UnionType>types [j]).types;
13671+ const length = sourceTypes .length;
13672+ constituents[j] = sourceTypes [n % length];
1365413673 n = Math.floor(n / length);
1365513674 }
1365613675 }
@@ -13738,8 +13757,10 @@ namespace ts {
1373813757 return neverType;
1373913758 }
1374013759
13741- function getLiteralTypeFromProperties(type: Type, include: TypeFlags) {
13742- return getUnionType(map(getPropertiesOfType(type), p => getLiteralTypeFromProperty(p, include)));
13760+ function getLiteralTypeFromProperties(type: Type, include: TypeFlags, includeOrigin: boolean) {
13761+ const origin = includeOrigin && (getObjectFlags(type) & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference) || type.aliasSymbol) ? createIndexType(type, /*stringsOnly*/ false) : undefined;
13762+ return getUnionType(map(getPropertiesOfType(type), p => getLiteralTypeFromProperty(p, include)), UnionReduction.Literal,
13763+ /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, origin);
1374313764 }
1374413765
1374513766 function getNonEnumNumberIndexInfo(type: Type) {
@@ -13748,6 +13769,7 @@ namespace ts {
1374813769 }
1374913770
1375013771 function getIndexType(type: Type, stringsOnly = keyofStringsOnly, noIndexSignatures?: boolean): Type {
13772+ const includeOrigin = stringsOnly === keyofStringsOnly && !noIndexSignatures;
1375113773 type = getReducedType(type);
1375213774 return type.flags & TypeFlags.Union ? getIntersectionType(map((<IntersectionType>type).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) :
1375313775 type.flags & TypeFlags.Intersection ? getUnionType(map((<IntersectionType>type).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) :
@@ -13756,10 +13778,10 @@ namespace ts {
1375613778 type === wildcardType ? wildcardType :
1375713779 type.flags & TypeFlags.Unknown ? neverType :
1375813780 type.flags & (TypeFlags.Any | TypeFlags.Never) ? keyofConstraintType :
13759- stringsOnly ? !noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? stringType : getLiteralTypeFromProperties(type, TypeFlags.StringLiteral) :
13760- !noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? getUnionType([stringType, numberType, getLiteralTypeFromProperties(type, TypeFlags.UniqueESSymbol)]) :
13761- getNonEnumNumberIndexInfo(type) ? getUnionType([numberType, getLiteralTypeFromProperties(type, TypeFlags.StringLiteral | TypeFlags.UniqueESSymbol)]) :
13762- getLiteralTypeFromProperties(type, TypeFlags.StringOrNumberLiteralOrUnique);
13781+ stringsOnly ? !noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? stringType : getLiteralTypeFromProperties(type, TypeFlags.StringLiteral, includeOrigin ) :
13782+ !noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? getUnionType([stringType, numberType, getLiteralTypeFromProperties(type, TypeFlags.UniqueESSymbol, includeOrigin )]) :
13783+ getNonEnumNumberIndexInfo(type) ? getUnionType([numberType, getLiteralTypeFromProperties(type, TypeFlags.StringLiteral | TypeFlags.UniqueESSymbol, includeOrigin )]) :
13784+ getLiteralTypeFromProperties(type, TypeFlags.StringOrNumberLiteralOrUnique, includeOrigin );
1376313785 }
1376413786
1376513787 function getExtractStringType(type: Type) {
@@ -15520,10 +15542,6 @@ namespace ts {
1552015542 return getConditionalType(root, mapper);
1552115543 }
1552215544
15523- function instantiateUnionOrigin(origin: UnionOrigin | undefined, mapper: TypeMapper) {
15524- return origin && { types: instantiateTypes(origin.types, mapper), isIntersection: origin.isIntersection };
15525- }
15526-
1552715545 function instantiateType(type: Type, mapper: TypeMapper | undefined): Type;
1552815546 function instantiateType(type: Type | undefined, mapper: TypeMapper | undefined): Type | undefined;
1552915547 function instantiateType(type: Type | undefined, mapper: TypeMapper | undefined): Type | undefined {
@@ -15564,12 +15582,13 @@ namespace ts {
1556415582 return type;
1556515583 }
1556615584 if (flags & TypeFlags.UnionOrIntersection) {
15567- const types = (<UnionOrIntersectionType>type).types;
15585+ const origin = type.flags & TypeFlags.Union ? (<UnionType>type).origin : undefined;
15586+ const types = origin && origin.flags & TypeFlags.UnionOrIntersection ? (<UnionOrIntersectionType>origin).types : (<UnionOrIntersectionType>type).types;
1556815587 const newTypes = instantiateTypes(types, mapper);
1556915588 return newTypes === types ? type :
15570- flags & TypeFlags.Intersection ?
15589+ flags & TypeFlags.Intersection || origin && origin.flags & TypeFlags.Intersection ?
1557115590 getIntersectionType(newTypes, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)) :
15572- getUnionType(newTypes, UnionReduction.Literal, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper), instantiateUnionOrigin((<UnionType>type).origin, mapper) );
15591+ getUnionType(newTypes, UnionReduction.Literal, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
1557315592 }
1557415593 if (flags & TypeFlags.Index) {
1557515594 return getIndexType(instantiateType((<IndexType>type).type, mapper));
0 commit comments