@@ -8533,6 +8533,15 @@ namespace ts {
8533
8533
return binarySearch(types, type, getTypeId, compareValues) >= 0;
8534
8534
}
8535
8535
8536
+ function insertType(types: Type[], type: Type): boolean {
8537
+ const index = binarySearch(types, type, getTypeId, compareValues);
8538
+ if (index < 0) {
8539
+ types.splice(~index, 0, type);
8540
+ return true;
8541
+ }
8542
+ return false;
8543
+ }
8544
+
8536
8545
// Return true if the given intersection type contains
8537
8546
// more than one unit type or,
8538
8547
// an object type and a nullable type (null or undefined), or
@@ -8700,7 +8709,7 @@ namespace ts {
8700
8709
includes & TypeFlags.Undefined ? includes & TypeFlags.NonWideningType ? undefinedType : undefinedWideningType :
8701
8710
neverType;
8702
8711
}
8703
- return getUnionTypeFromSortedList(typeSet, includes & TypeFlags.NotUnit ? 0 : TypeFlags.UnionOfUnitTypes , aliasSymbol, aliasTypeArguments);
8712
+ return getUnionTypeFromSortedList(typeSet, includes & TypeFlags.NotPrimitiveUnion ? 0 : TypeFlags.UnionOfPrimitiveTypes , aliasSymbol, aliasTypeArguments);
8704
8713
}
8705
8714
8706
8715
function getUnionTypePredicate(signatures: ReadonlyArray<Signature>): TypePredicate | undefined {
@@ -8823,26 +8832,62 @@ namespace ts {
8823
8832
}
8824
8833
}
8825
8834
8826
- // When intersecting unions of unit types we can simply intersect based on type identity.
8827
- // Here we remove all unions of unit types from the given list and replace them with a
8828
- // a single union containing an intersection of the unit types.
8829
- function intersectUnionsOfUnitTypes(types: Type[]) {
8830
- const unionIndex = findIndex(types, t => (t.flags & TypeFlags.UnionOfUnitTypes) !== 0);
8831
- const unionType = <UnionType>types[unionIndex];
8832
- let intersection = unionType.types;
8833
- let i = types.length - 1;
8834
- while (i > unionIndex) {
8835
+ // Check that the given type has a match in every union. A given type is matched by
8836
+ // an identical type, and a literal type is additionally matched by its corresponding
8837
+ // primitive type.
8838
+ function eachUnionContains(unionTypes: UnionType[], type: Type) {
8839
+ for (const u of unionTypes) {
8840
+ if (!containsType(u.types, type)) {
8841
+ const primitive = type.flags & TypeFlags.StringLiteral ? stringType :
8842
+ type.flags & TypeFlags.NumberLiteral ? numberType :
8843
+ type.flags & TypeFlags.UniqueESSymbol ? esSymbolType :
8844
+ undefined;
8845
+ if (!primitive || !containsType(u.types, primitive)) {
8846
+ return false;
8847
+ }
8848
+ }
8849
+ }
8850
+ return true;
8851
+ }
8852
+
8853
+ // Remove all unions of primitive types from the given list and replace them with a
8854
+ // single union containing an intersection of those primitive types.
8855
+ function intersectUnionsOfPrimitiveTypes(types: Type[]) {
8856
+ let unionTypes: UnionType[] | undefined;
8857
+ const index = findIndex(types, t => (t.flags & TypeFlags.UnionOfPrimitiveTypes) !== 0);
8858
+ let i = index + 1;
8859
+ // Remove all but the first union of primitive types and collect them in
8860
+ // the unionTypes array.
8861
+ while (i < types.length) {
8835
8862
const t = types[i];
8836
- if (t.flags & TypeFlags.UnionOfUnitTypes ) {
8837
- intersection = filter(intersection, u => containsType(( <UnionType>t). types, u) );
8863
+ if (t.flags & TypeFlags.UnionOfPrimitiveTypes ) {
8864
+ (unionTypes || (unionTypes = [ <UnionType>types[index]])).push(<UnionType>t );
8838
8865
orderedRemoveItemAt(types, i);
8839
8866
}
8840
- i--;
8867
+ else {
8868
+ i++;
8869
+ }
8841
8870
}
8842
- if (intersection === unionType.types) {
8871
+ // Return false if there was only one union of primitive types
8872
+ if (!unionTypes) {
8843
8873
return false;
8844
8874
}
8845
- types[unionIndex] = getUnionTypeFromSortedList(intersection, unionType.flags & TypeFlags.UnionOfUnitTypes);
8875
+ // We have more than one union of primitive types, now intersect them. For each
8876
+ // type in each union we check if the type is matched in every union and if so
8877
+ // we include it in the result.
8878
+ const checked: Type[] = [];
8879
+ const result: Type[] = [];
8880
+ for (const u of unionTypes) {
8881
+ for (const t of u.types) {
8882
+ if (insertType(checked, t)) {
8883
+ if (eachUnionContains(unionTypes, t)) {
8884
+ insertType(result, t);
8885
+ }
8886
+ }
8887
+ }
8888
+ }
8889
+ // Finally replace the first union with the result
8890
+ types[index] = getUnionTypeFromSortedList(result, TypeFlags.UnionOfPrimitiveTypes);
8846
8891
return true;
8847
8892
}
8848
8893
@@ -8883,7 +8928,7 @@ namespace ts {
8883
8928
return typeSet[0];
8884
8929
}
8885
8930
if (includes & TypeFlags.Union) {
8886
- if (includes & TypeFlags.UnionOfUnitTypes && intersectUnionsOfUnitTypes (typeSet)) {
8931
+ if (includes & TypeFlags.UnionOfPrimitiveTypes && intersectUnionsOfPrimitiveTypes (typeSet)) {
8887
8932
// When the intersection creates a reduced set (which might mean that *all* union types have
8888
8933
// disappeared), we restart the operation to get a new set of combined flags. Once we have
8889
8934
// reduced we'll never reduce again, so this occurs at most once.
@@ -13980,7 +14025,7 @@ namespace ts {
13980
14025
if (type.flags & TypeFlags.Union) {
13981
14026
const types = (<UnionType>type).types;
13982
14027
const filtered = filter(types, f);
13983
- return filtered === types ? type : getUnionTypeFromSortedList(filtered, type.flags & TypeFlags.UnionOfUnitTypes );
14028
+ return filtered === types ? type : getUnionTypeFromSortedList(filtered, type.flags & TypeFlags.UnionOfPrimitiveTypes );
13984
14029
}
13985
14030
return f(type) ? type : neverType;
13986
14031
}
0 commit comments