Skip to content

Commit 9458f8a

Browse files
authored
Consistent narrowing to 'never' in conditional and switch statements (#39191)
* Allow unions and unit types to narrow to 'never' * Remove odd check for TypeFlags.NotUnionOrUnit * Accept new baselines * Accept new API baselines
1 parent b448540 commit 9458f8a

7 files changed

+15
-39
lines changed

src/compiler/checker.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20580,7 +20580,7 @@ namespace ts {
2058020580
}
2058120581

2058220582
function createFlowType(type: Type, incomplete: boolean): FlowType {
20583-
return incomplete ? { flags: 0, type } : type;
20583+
return incomplete ? { flags: 0, type: type.flags & TypeFlags.Never ? silentNeverType : type } : type;
2058420584
}
2058520585

2058620586
// An evolving array type tracks the element types that have so far been seen in an
@@ -21168,9 +21168,7 @@ namespace ts {
2116821168
if (narrowedType === nonEvolvingType) {
2116921169
return flowType;
2117021170
}
21171-
const incomplete = isIncomplete(flowType);
21172-
const resultType = incomplete && narrowedType.flags & TypeFlags.Never ? silentNeverType : narrowedType;
21173-
return createFlowType(resultType, incomplete);
21171+
return createFlowType(narrowedType, isIncomplete(flowType));
2117421172
}
2117521173

2117621174
function getTypeAtSwitchClause(flow: FlowSwitchClause): FlowType {
@@ -21508,15 +21506,11 @@ namespace ts {
2150821506
assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined;
2150921507
return getTypeWithFacts(type, facts);
2151021508
}
21511-
if (type.flags & TypeFlags.NotUnionOrUnit) {
21512-
return type;
21513-
}
2151421509
if (assumeTrue) {
2151521510
const filterFn: (t: Type) => boolean = operator === SyntaxKind.EqualsEqualsToken ?
2151621511
(t => areTypesComparable(t, valueType) || isCoercibleUnderDoubleEquals(t, valueType)) :
2151721512
t => areTypesComparable(t, valueType);
21518-
const narrowedType = filterType(type, filterFn);
21519-
return narrowedType.flags & TypeFlags.Never ? type : replacePrimitivesWithLiterals(narrowedType, valueType);
21513+
return replacePrimitivesWithLiterals(filterType(type, filterFn), valueType);
2152021514
}
2152121515
if (isUnitType(valueType)) {
2152221516
const regularType = getRegularTypeOfLiteralType(valueType);

src/compiler/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4861,7 +4861,6 @@ namespace ts {
48614861
// 'Narrowable' types are types where narrowing actually narrows.
48624862
// This *should* be every type other than null, undefined, void, and never
48634863
Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive,
4864-
NotUnionOrUnit = Any | Unknown | ESSymbol | Object | NonPrimitive,
48654864
/* @internal */
48664865
NotPrimitiveUnion = Any | Unknown | Enum | Void | Never | StructuredOrInstantiable,
48674866
// The following flags are aggregated during union and intersection type construction

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2459,7 +2459,6 @@ declare namespace ts {
24592459
Instantiable = 63176704,
24602460
StructuredOrInstantiable = 66846720,
24612461
Narrowable = 133970943,
2462-
NotUnionOrUnit = 67637251,
24632462
}
24642463
export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression;
24652464
export interface Type {

tests/baselines/reference/api/typescript.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2459,7 +2459,6 @@ declare namespace ts {
24592459
Instantiable = 63176704,
24602460
StructuredOrInstantiable = 66846720,
24612461
Narrowable = 133970943,
2462-
NotUnionOrUnit = 67637251,
24632462
}
24642463
export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression;
24652464
export interface Type {

tests/baselines/reference/discriminatedUnionTypes1.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ function f3(m: Message) {
320320
>"X" : "X"
321321

322322
m; // never
323-
>m : Message
323+
>m : never
324324
}
325325
}
326326

tests/baselines/reference/equalityWithIntersectionTypes01.errors.txt

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
tests/cases/conformance/types/typeRelationships/comparable/equalityWithIntersectionTypes01.ts(17,5): error TS2367: This condition will always return 'false' since the types 'I1 & I3' and 'I2' have no overlap.
22
tests/cases/conformance/types/typeRelationships/comparable/equalityWithIntersectionTypes01.ts(17,16): error TS2367: This condition will always return 'false' since the types 'I2' and 'I1 & I3' have no overlap.
33
tests/cases/conformance/types/typeRelationships/comparable/equalityWithIntersectionTypes01.ts(19,10): error TS2367: This condition will always return 'true' since the types 'I1 & I3' and 'I2' have no overlap.
4-
tests/cases/conformance/types/typeRelationships/comparable/equalityWithIntersectionTypes01.ts(19,21): error TS2367: This condition will always return 'true' since the types 'I2' and 'I1 & I3' have no overlap.
5-
tests/cases/conformance/types/typeRelationships/comparable/equalityWithIntersectionTypes01.ts(21,10): error TS2367: This condition will always return 'false' since the types 'I1 & I3' and 'I2' have no overlap.
6-
tests/cases/conformance/types/typeRelationships/comparable/equalityWithIntersectionTypes01.ts(21,20): error TS2367: This condition will always return 'false' since the types 'I2' and 'I1 & I3' have no overlap.
7-
tests/cases/conformance/types/typeRelationships/comparable/equalityWithIntersectionTypes01.ts(23,10): error TS2367: This condition will always return 'true' since the types 'I1 & I3' and 'I2' have no overlap.
8-
tests/cases/conformance/types/typeRelationships/comparable/equalityWithIntersectionTypes01.ts(23,20): error TS2367: This condition will always return 'true' since the types 'I2' and 'I1 & I3' have no overlap.
94

105

11-
==== tests/cases/conformance/types/typeRelationships/comparable/equalityWithIntersectionTypes01.ts (8 errors) ====
6+
==== tests/cases/conformance/types/typeRelationships/comparable/equalityWithIntersectionTypes01.ts (3 errors) ====
127
interface I1 {
138
p1: number
149
}
@@ -34,18 +29,8 @@ tests/cases/conformance/types/typeRelationships/comparable/equalityWithIntersect
3429
else if (y !== z || z !== y) {
3530
~~~~~~~
3631
!!! error TS2367: This condition will always return 'true' since the types 'I1 & I3' and 'I2' have no overlap.
37-
~~~~~~~
38-
!!! error TS2367: This condition will always return 'true' since the types 'I2' and 'I1 & I3' have no overlap.
3932
}
4033
else if (y == z || z == y) {
41-
~~~~~~
42-
!!! error TS2367: This condition will always return 'false' since the types 'I1 & I3' and 'I2' have no overlap.
43-
~~~~~~
44-
!!! error TS2367: This condition will always return 'false' since the types 'I2' and 'I1 & I3' have no overlap.
4534
}
4635
else if (y != z || z != y) {
47-
~~~~~~
48-
!!! error TS2367: This condition will always return 'true' since the types 'I1 & I3' and 'I2' have no overlap.
49-
~~~~~~
50-
!!! error TS2367: This condition will always return 'true' since the types 'I2' and 'I1 & I3' have no overlap.
5136
}

tests/baselines/reference/equalityWithIntersectionTypes01.types

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,24 +47,24 @@ else if (y !== z || z !== y) {
4747
>y : I1 & I3
4848
>z : I2
4949
>z !== y : boolean
50-
>z : I2
51-
>y : I1 & I3
50+
>z : never
51+
>y : never
5252
}
5353
else if (y == z || z == y) {
5454
>y == z || z == y : boolean
5555
>y == z : boolean
56-
>y : I1 & I3
57-
>z : I2
56+
>y : never
57+
>z : never
5858
>z == y : boolean
59-
>z : I2
60-
>y : I1 & I3
59+
>z : never
60+
>y : never
6161
}
6262
else if (y != z || z != y) {
6363
>y != z || z != y : boolean
6464
>y != z : boolean
65-
>y : I1 & I3
66-
>z : I2
65+
>y : never
66+
>z : never
6767
>z != y : boolean
68-
>z : I2
69-
>y : I1 & I3
68+
>z : never
69+
>y : never
7070
}

0 commit comments

Comments
 (0)