@@ -4553,17 +4553,17 @@ namespace ts {
4553
4553
? symbolToTypeNode(type.symbol, context, SymbolFlags.Type)
4554
4554
: factory.createTypeReferenceNode(factory.createIdentifier("?"), /*typeArguments*/ undefined);
4555
4555
}
4556
+ if (type.flags & TypeFlags.Union && (<UnionType>type).origin) {
4557
+ type = (<UnionType>type).origin!;
4558
+ }
4556
4559
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;
4561
4561
if (length(types) === 1) {
4562
4562
return typeToTypeNodeHelper(types[0], context);
4563
4563
}
4564
4564
const typeNodes = mapToTypeNodes(types, context, /*isBareList*/ true);
4565
4565
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);
4567
4567
}
4568
4568
else {
4569
4569
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowEmptyUnionOrIntersection)) {
@@ -13256,7 +13256,7 @@ namespace ts {
13256
13256
// expression constructs such as array literals and the || and ?: operators). Named types can
13257
13257
// circularly reference themselves and therefore cannot be subtype reduced during their declaration.
13258
13258
// 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 {
13260
13260
if (types.length === 0) {
13261
13261
return neverType;
13262
13262
}
@@ -13298,14 +13298,17 @@ namespace ts {
13298
13298
reducedTypes.push(t);
13299
13299
}
13300
13300
}
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];
13306
13303
}
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);
13309
13312
}
13310
13313
}
13311
13314
const objectFlags = (includes & TypeFlags.NotPrimitiveUnion ? 0 : ObjectFlags.PrimitiveUnion) |
@@ -13345,24 +13348,34 @@ namespace ts {
13345
13348
return a.kind === b.kind && a.parameterIndex === b.parameterIndex;
13346
13349
}
13347
13350
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
+
13348
13361
// 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 {
13350
13363
if (types.length === 0) {
13351
13364
return neverType;
13352
13365
}
13353
13366
if (types.length === 1) {
13354
13367
return types[0];
13355
13368
}
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)}` : "");
13357
13374
let type = unionTypes.get(id);
13358
13375
if (!type) {
13359
- type = <UnionType>createType(TypeFlags.Union);
13376
+ type = createUnionType(types, aliasSymbol, aliasTypeArguments, origin);
13377
+ type.objectFlags |= objectFlags;
13360
13378
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;
13366
13379
}
13367
13380
return type;
13368
13381
}
@@ -13611,14 +13624,16 @@ namespace ts {
13611
13624
result = getUnionType([getIntersectionType(typeSet), nullType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments);
13612
13625
}
13613
13626
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.
13617
13630
if (!checkCrossProductUnion(typeSet)) {
13618
13631
return errorType;
13619
13632
}
13620
13633
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;
13622
13637
result = getUnionType(constituents, UnionReduction.Literal, aliasSymbol, aliasTypeArguments, origin);
13623
13638
}
13624
13639
}
@@ -13630,8 +13645,12 @@ namespace ts {
13630
13645
return result;
13631
13646
}
13632
13647
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
+
13633
13652
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);
13635
13654
if (size >= 100000) {
13636
13655
tracing.instant(tracing.Phase.CheckTypes, "checkCrossProductUnion_DepthLimit", { typeIds: types.map(t => t.id), size });
13637
13656
error(currentNode, Diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent);
@@ -13640,17 +13659,17 @@ namespace ts {
13640
13659
return true;
13641
13660
}
13642
13661
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);
13645
13664
const intersections: Type[] = [];
13646
13665
for (let i = 0; i < count; i++) {
13647
- const constituents = typeSet .slice();
13666
+ const constituents = types .slice();
13648
13667
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];
13654
13673
n = Math.floor(n / length);
13655
13674
}
13656
13675
}
@@ -13738,8 +13757,10 @@ namespace ts {
13738
13757
return neverType;
13739
13758
}
13740
13759
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);
13743
13764
}
13744
13765
13745
13766
function getNonEnumNumberIndexInfo(type: Type) {
@@ -13748,6 +13769,7 @@ namespace ts {
13748
13769
}
13749
13770
13750
13771
function getIndexType(type: Type, stringsOnly = keyofStringsOnly, noIndexSignatures?: boolean): Type {
13772
+ const includeOrigin = stringsOnly === keyofStringsOnly && !noIndexSignatures;
13751
13773
type = getReducedType(type);
13752
13774
return type.flags & TypeFlags.Union ? getIntersectionType(map((<IntersectionType>type).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) :
13753
13775
type.flags & TypeFlags.Intersection ? getUnionType(map((<IntersectionType>type).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) :
@@ -13756,10 +13778,10 @@ namespace ts {
13756
13778
type === wildcardType ? wildcardType :
13757
13779
type.flags & TypeFlags.Unknown ? neverType :
13758
13780
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 );
13763
13785
}
13764
13786
13765
13787
function getExtractStringType(type: Type) {
@@ -15520,10 +15542,6 @@ namespace ts {
15520
15542
return getConditionalType(root, mapper);
15521
15543
}
15522
15544
15523
- function instantiateUnionOrigin(origin: UnionOrigin | undefined, mapper: TypeMapper) {
15524
- return origin && { types: instantiateTypes(origin.types, mapper), isIntersection: origin.isIntersection };
15525
- }
15526
-
15527
15545
function instantiateType(type: Type, mapper: TypeMapper | undefined): Type;
15528
15546
function instantiateType(type: Type | undefined, mapper: TypeMapper | undefined): Type | undefined;
15529
15547
function instantiateType(type: Type | undefined, mapper: TypeMapper | undefined): Type | undefined {
@@ -15564,12 +15582,13 @@ namespace ts {
15564
15582
return type;
15565
15583
}
15566
15584
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;
15568
15587
const newTypes = instantiateTypes(types, mapper);
15569
15588
return newTypes === types ? type :
15570
- flags & TypeFlags.Intersection ?
15589
+ flags & TypeFlags.Intersection || origin && origin.flags & TypeFlags.Intersection ?
15571
15590
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));
15573
15592
}
15574
15593
if (flags & TypeFlags.Index) {
15575
15594
return getIndexType(instantiateType((<IndexType>type).type, mapper));
0 commit comments