diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5c187d6ad8a..4bd2dd54908 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9012,7 +9012,7 @@ namespace ts { neverType; } } - return getUnionTypeFromSortedList(typeSet, includes & TypeFlags.NotPrimitiveUnion ? 0 : TypeFlags.UnionOfPrimitiveTypes, aliasSymbol, aliasTypeArguments); + return getUnionTypeFromSortedList(typeSet, !(includes & TypeFlags.NotPrimitiveUnion), aliasSymbol, aliasTypeArguments); } function getUnionTypePredicate(signatures: ReadonlyArray): TypePredicate | undefined { @@ -9052,7 +9052,7 @@ namespace ts { } // This function assumes the constituent type list is sorted and deduplicated. - function getUnionTypeFromSortedList(types: Type[], unionOfUnitTypes: TypeFlags, aliasSymbol?: Symbol, aliasTypeArguments?: ReadonlyArray): Type { + function getUnionTypeFromSortedList(types: Type[], primitiveTypesOnly: boolean, aliasSymbol?: Symbol, aliasTypeArguments?: ReadonlyArray): Type { if (types.length === 0) { return neverType; } @@ -9063,9 +9063,10 @@ namespace ts { let type = unionTypes.get(id); if (!type) { const propagatedFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); - type = createType(TypeFlags.Union | propagatedFlags | unionOfUnitTypes); + type = createType(TypeFlags.Union | propagatedFlags); unionTypes.set(id, type); type.types = types; + type.primitiveTypesOnly = primitiveTypesOnly; /* Note: This is the alias symbol (or lack thereof) that we see when we first encounter this union type. For aliases of identical unions, eg `type T = A | B; type U = A | B`, the symbol of the first alias encountered is the aliasSymbol. @@ -9157,13 +9158,16 @@ namespace ts { // other unions and return true. Otherwise, do nothing and return false. function intersectUnionsOfPrimitiveTypes(types: Type[]) { let unionTypes: UnionType[] | undefined; - const index = findIndex(types, t => (t.flags & TypeFlags.UnionOfPrimitiveTypes) !== 0); + const index = findIndex(types, t => !!(t.flags & TypeFlags.Union) && (t).primitiveTypesOnly); + if (index < 0) { + return false; + } let i = index + 1; // Remove all but the first union of primitive types and collect them in // the unionTypes array. while (i < types.length) { const t = types[i]; - if (t.flags & TypeFlags.UnionOfPrimitiveTypes) { + if (t.flags & TypeFlags.Union && (t).primitiveTypesOnly) { (unionTypes || (unionTypes = [types[index]])).push(t); orderedRemoveItemAt(types, i); } @@ -9190,7 +9194,7 @@ namespace ts { } } // Finally replace the first union with the result - types[index] = getUnionTypeFromSortedList(result, TypeFlags.UnionOfPrimitiveTypes); + types[index] = getUnionTypeFromSortedList(result, /*primitiveTypesOnly*/ true); return true; } @@ -9232,7 +9236,7 @@ namespace ts { return typeSet[0]; } if (includes & TypeFlags.Union) { - if (includes & TypeFlags.UnionOfPrimitiveTypes && intersectUnionsOfPrimitiveTypes(typeSet)) { + if (intersectUnionsOfPrimitiveTypes(typeSet)) { // When the intersection creates a reduced set (which might mean that *all* union types have // disappeared), we restart the operation to get a new set of combined flags. Once we have // reduced we'll never reduce again, so this occurs at most once. @@ -11809,12 +11813,14 @@ namespace ts { } // Keep this up-to-date with the same logic within `getApparentTypeOfContextualType`, since they should behave similarly - function findMatchingDiscriminantType(source: Type, target: UnionOrIntersectionType) { - const sourceProperties = getPropertiesOfObjectType(source); - if (sourceProperties) { - const sourcePropertiesFiltered = findDiscriminantProperties(sourceProperties, target); - if (sourcePropertiesFiltered) { - return discriminateTypeByDiscriminableItems(target, map(sourcePropertiesFiltered, p => ([() => getTypeOfSymbol(p), p.escapedName] as [() => Type, __String])), isRelatedTo); + function findMatchingDiscriminantType(source: Type, target: Type) { + if (target.flags & TypeFlags.Union) { + const sourceProperties = getPropertiesOfObjectType(source); + if (sourceProperties) { + const sourcePropertiesFiltered = findDiscriminantProperties(sourceProperties, target); + if (sourcePropertiesFiltered) { + return discriminateTypeByDiscriminableItems(target, map(sourcePropertiesFiltered, p => ([() => getTypeOfSymbol(p), p.escapedName] as [() => Type, __String])), isRelatedTo); + } } } return undefined; @@ -14846,7 +14852,7 @@ namespace ts { if (type.flags & TypeFlags.Union) { const types = (type).types; const filtered = filter(types, f); - return filtered === types ? type : getUnionTypeFromSortedList(filtered, type.flags & TypeFlags.UnionOfPrimitiveTypes); + return filtered === types ? type : getUnionTypeFromSortedList(filtered, (type).primitiveTypesOnly); } return f(type) ? type : neverType; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 46a5ed91275..0ca4de3f537 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3838,13 +3838,11 @@ namespace ts { /* @internal */ FreshLiteral = 1 << 27, // Fresh literal or unique type /* @internal */ - UnionOfPrimitiveTypes = 1 << 28, // Type is union of primitive types + ContainsWideningType = 1 << 28, // Type is or contains undefined or null widening type /* @internal */ - ContainsWideningType = 1 << 29, // Type is or contains undefined or null widening type + ContainsObjectLiteral = 1 << 29, // Type is or contains object literal type /* @internal */ - ContainsObjectLiteral = 1 << 30, // Type is or contains object literal type - /* @internal */ - ContainsAnyFunctionType = 1 << 31, // Type is or contains the anyFunctionType + ContainsAnyFunctionType = 1 << 30, // Type is or contains the anyFunctionType /* @internal */ AnyOrUnknown = Any | Unknown, @@ -4077,7 +4075,10 @@ namespace ts { couldContainTypeVariables: boolean; } - export interface UnionType extends UnionOrIntersectionType { } + export interface UnionType extends UnionOrIntersectionType { + /* @internal */ + primitiveTypesOnly: boolean; + } export interface IntersectionType extends UnionOrIntersectionType { /* @internal */