Skip to content

Commit d4c9f24

Browse files
committed
Intersections of disjoint types become never upon construction
1 parent 8705844 commit d4c9f24

File tree

2 files changed

+26
-36
lines changed

2 files changed

+26
-36
lines changed

src/compiler/checker.ts

Lines changed: 25 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9522,44 +9522,13 @@ namespace ts {
95229522
return false;
95239523
}
95249524

9525-
// Return true if the given intersection type contains
9526-
// more than one unit type or,
9527-
// an object type and a nullable type (null or undefined), or
9528-
// a string-like type and a type known to be non-string-like, or
9529-
// a number-like type and a type known to be non-number-like, or
9530-
// a symbol-like type and a type known to be non-symbol-like, or
9531-
// a void-like type and a type known to be non-void-like, or
9532-
// a non-primitive type and a type known to be primitive.
9533-
function isEmptyIntersectionType(type: IntersectionType) {
9534-
let combined: TypeFlags = 0;
9535-
for (const t of type.types) {
9536-
if (t.flags & TypeFlags.Unit && combined & TypeFlags.Unit) {
9537-
return true;
9538-
}
9539-
combined |= t.flags;
9540-
if (combined & TypeFlags.Nullable && combined & (TypeFlags.Object | TypeFlags.NonPrimitive) ||
9541-
combined & TypeFlags.NonPrimitive && combined & (TypeFlags.DisjointDomains & ~TypeFlags.NonPrimitive) ||
9542-
combined & TypeFlags.StringLike && combined & (TypeFlags.DisjointDomains & ~TypeFlags.StringLike) ||
9543-
combined & TypeFlags.NumberLike && combined & (TypeFlags.DisjointDomains & ~TypeFlags.NumberLike) ||
9544-
combined & TypeFlags.BigIntLike && combined & (TypeFlags.DisjointDomains & ~TypeFlags.BigIntLike) ||
9545-
combined & TypeFlags.ESSymbolLike && combined & (TypeFlags.DisjointDomains & ~TypeFlags.ESSymbolLike) ||
9546-
combined & TypeFlags.VoidLike && combined & (TypeFlags.DisjointDomains & ~TypeFlags.VoidLike)) {
9547-
return true;
9548-
}
9549-
}
9550-
return false;
9551-
}
9552-
95539525
function addTypeToUnion(typeSet: Type[], includes: TypeFlags, type: Type) {
95549526
const flags = type.flags;
95559527
if (flags & TypeFlags.Union) {
95569528
return addTypesToUnion(typeSet, includes, (<UnionType>type).types);
95579529
}
9558-
// We ignore 'never' types in unions. Likewise, we ignore intersections of unit types as they are
9559-
// another form of 'never' (in that they have an empty value domain). We could in theory turn
9560-
// intersections of unit types into 'never' upon construction, but deferring the reduction makes it
9561-
// easier to reason about their origin.
9562-
if (!(flags & TypeFlags.Never || flags & TypeFlags.Intersection && isEmptyIntersectionType(<IntersectionType>type))) {
9530+
// We ignore 'never' types in unions
9531+
if (!(flags & TypeFlags.Never)) {
95639532
includes |= flags & TypeFlags.IncludesMask;
95649533
if (flags & TypeFlags.StructuredOrInstantiable) includes |= TypeFlags.IncludesStructuredOrInstantiable;
95659534
if (type === wildcardType) includes |= TypeFlags.IncludesWildcard;
@@ -9783,13 +9752,18 @@ namespace ts {
97839752
}
97849753
}
97859754
else {
9786-
includes |= flags & TypeFlags.IncludesMask;
97879755
if (flags & TypeFlags.AnyOrUnknown) {
97889756
if (type === wildcardType) includes |= TypeFlags.IncludesWildcard;
97899757
}
97909758
else if ((strictNullChecks || !(flags & TypeFlags.Nullable)) && !contains(typeSet, type)) {
9759+
if (type.flags & TypeFlags.Unit && includes & TypeFlags.Unit) {
9760+
// We have seen two distinct unit types which means we should reduce to an
9761+
// empty intersection. Adding TypeFlags.NonPrimitive causes that to happen.
9762+
includes |= TypeFlags.NonPrimitive;
9763+
}
97919764
typeSet.push(type);
97929765
}
9766+
includes |= flags & TypeFlags.IncludesMask;
97939767
}
97949768
return includes;
97959769
}
@@ -9905,7 +9879,23 @@ namespace ts {
99059879
function getIntersectionType(types: ReadonlyArray<Type>, aliasSymbol?: Symbol, aliasTypeArguments?: ReadonlyArray<Type>): Type {
99069880
const typeSet: Type[] = [];
99079881
const includes = addTypesToIntersection(typeSet, 0, types);
9908-
if (includes & TypeFlags.Never) {
9882+
// An intersection type is considered empty if it contains
9883+
// the type never, or
9884+
// more than one unit type or,
9885+
// an object type and a nullable type (null or undefined), or
9886+
// a string-like type and a type known to be non-string-like, or
9887+
// a number-like type and a type known to be non-number-like, or
9888+
// a symbol-like type and a type known to be non-symbol-like, or
9889+
// a void-like type and a type known to be non-void-like, or
9890+
// a non-primitive type and a type known to be primitive.
9891+
if (includes & TypeFlags.Never ||
9892+
includes & TypeFlags.Nullable && includes & (TypeFlags.Object | TypeFlags.NonPrimitive | TypeFlags.IncludesEmptyObject) ||
9893+
includes & TypeFlags.NonPrimitive && includes & (TypeFlags.DisjointDomains & ~TypeFlags.NonPrimitive) ||
9894+
includes & TypeFlags.StringLike && includes & (TypeFlags.DisjointDomains & ~TypeFlags.StringLike) ||
9895+
includes & TypeFlags.NumberLike && includes & (TypeFlags.DisjointDomains & ~TypeFlags.NumberLike) ||
9896+
includes & TypeFlags.BigIntLike && includes & (TypeFlags.DisjointDomains & ~TypeFlags.BigIntLike) ||
9897+
includes & TypeFlags.ESSymbolLike && includes & (TypeFlags.DisjointDomains & ~TypeFlags.ESSymbolLike) ||
9898+
includes & TypeFlags.VoidLike && includes & (TypeFlags.DisjointDomains & ~TypeFlags.VoidLike)) {
99099899
return neverType;
99109900
}
99119901
if (includes & TypeFlags.Any) {

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3983,7 +3983,7 @@ namespace ts {
39833983
NotPrimitiveUnion = Any | Unknown | Enum | Void | Never | StructuredOrInstantiable,
39843984
// The following flags are aggregated during union and intersection type construction
39853985
/* @internal */
3986-
IncludesMask = Any | Unknown | Primitive | Never | Object | Union,
3986+
IncludesMask = Any | Unknown | Primitive | Never | Object | Union | NonPrimitive,
39873987
// The following flags are used for different purposes during union and intersection type construction
39883988
/* @internal */
39893989
IncludesStructuredOrInstantiable = TypeParameter,

0 commit comments

Comments
 (0)