diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index aa0091f99523a..c796cde271408 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -131,6 +131,7 @@ namespace ts { UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy, NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy, EmptyObjectStrictFacts = All & ~(EQUndefined | EQNull | EQUndefinedOrNull), + AllTypeofNE = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined, EmptyObjectFacts = All, } @@ -28194,6 +28195,10 @@ namespace ts { // notEqualFacts states that the type of the switched value is not equal to every type in the switch. const notEqualFacts = getFactsFromTypeofSwitch(0, 0, witnesses, /*hasDefault*/ true); const type = getBaseConstraintOfType(operandType) || operandType; + // Take any/unknown as a special condition. Or maybe we could change `type` to a union containing all primitive types. + if (type.flags & TypeFlags.AnyOrUnknown) { + return (TypeFacts.AllTypeofNE & notEqualFacts) === TypeFacts.AllTypeofNE; + } return !!(filterType(type, t => (getTypeFacts(t) & notEqualFacts) === notEqualFacts).flags & TypeFlags.Never); } const type = getTypeOfExpression(node.expression); diff --git a/tests/baselines/reference/unreachableSwitchTypeofAny.errors.txt b/tests/baselines/reference/unreachableSwitchTypeofAny.errors.txt new file mode 100644 index 0000000000000..0b6b15ea49559 --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofAny.errors.txt @@ -0,0 +1,19 @@ +tests/cases/compiler/unreachable.ts(12,5): error TS7027: Unreachable code detected. + + +==== tests/cases/compiler/unreachable.ts (1 errors) ==== + const unreachable = (x: any): number => { + switch (typeof x) { + case 'string': return 0 + case 'number': return 0 + case 'bigint': return 0 + case 'boolean': return 0 + case 'symbol': return 0 + case 'undefined': return 0 + case 'object': return 0 + case 'function': return 0 + } + x; + ~~ +!!! error TS7027: Unreachable code detected. + } \ No newline at end of file diff --git a/tests/baselines/reference/unreachableSwitchTypeofAny.js b/tests/baselines/reference/unreachableSwitchTypeofAny.js new file mode 100644 index 0000000000000..288c4d458caed --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofAny.js @@ -0,0 +1,29 @@ +//// [unreachable.ts] +const unreachable = (x: any): number => { + switch (typeof x) { + case 'string': return 0 + case 'number': return 0 + case 'bigint': return 0 + case 'boolean': return 0 + case 'symbol': return 0 + case 'undefined': return 0 + case 'object': return 0 + case 'function': return 0 + } + x; +} + +//// [unreachable.js] +var unreachable = function (x) { + switch (typeof x) { + case 'string': return 0; + case 'number': return 0; + case 'bigint': return 0; + case 'boolean': return 0; + case 'symbol': return 0; + case 'undefined': return 0; + case 'object': return 0; + case 'function': return 0; + } + x; +}; diff --git a/tests/baselines/reference/unreachableSwitchTypeofAny.symbols b/tests/baselines/reference/unreachableSwitchTypeofAny.symbols new file mode 100644 index 0000000000000..9052798faf790 --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofAny.symbols @@ -0,0 +1,20 @@ +=== tests/cases/compiler/unreachable.ts === +const unreachable = (x: any): number => { +>unreachable : Symbol(unreachable, Decl(unreachable.ts, 0, 5)) +>x : Symbol(x, Decl(unreachable.ts, 0, 21)) + + switch (typeof x) { +>x : Symbol(x, Decl(unreachable.ts, 0, 21)) + + case 'string': return 0 + case 'number': return 0 + case 'bigint': return 0 + case 'boolean': return 0 + case 'symbol': return 0 + case 'undefined': return 0 + case 'object': return 0 + case 'function': return 0 + } + x; +>x : Symbol(x, Decl(unreachable.ts, 0, 21)) +} diff --git a/tests/baselines/reference/unreachableSwitchTypeofAny.types b/tests/baselines/reference/unreachableSwitchTypeofAny.types new file mode 100644 index 0000000000000..8846c829ec5a8 --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofAny.types @@ -0,0 +1,45 @@ +=== tests/cases/compiler/unreachable.ts === +const unreachable = (x: any): number => { +>unreachable : (x: any) => number +>(x: any): number => { switch (typeof x) { case 'string': return 0 case 'number': return 0 case 'bigint': return 0 case 'boolean': return 0 case 'symbol': return 0 case 'undefined': return 0 case 'object': return 0 case 'function': return 0 } x;} : (x: any) => number +>x : any + + switch (typeof x) { +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : any + + case 'string': return 0 +>'string' : "string" +>0 : 0 + + case 'number': return 0 +>'number' : "number" +>0 : 0 + + case 'bigint': return 0 +>'bigint' : "bigint" +>0 : 0 + + case 'boolean': return 0 +>'boolean' : "boolean" +>0 : 0 + + case 'symbol': return 0 +>'symbol' : "symbol" +>0 : 0 + + case 'undefined': return 0 +>'undefined' : "undefined" +>0 : 0 + + case 'object': return 0 +>'object' : "object" +>0 : 0 + + case 'function': return 0 +>'function' : "function" +>0 : 0 + } + x; +>x : any +} diff --git a/tests/baselines/reference/unreachableSwitchTypeofUnknown.errors.txt b/tests/baselines/reference/unreachableSwitchTypeofUnknown.errors.txt new file mode 100644 index 0000000000000..324e97d441f69 --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofUnknown.errors.txt @@ -0,0 +1,19 @@ +tests/cases/compiler/unreachable.ts(12,5): error TS7027: Unreachable code detected. + + +==== tests/cases/compiler/unreachable.ts (1 errors) ==== + const unreachable = (x: unknown): number => { + switch (typeof x) { + case 'string': return 0 + case 'number': return 0 + case 'bigint': return 0 + case 'boolean': return 0 + case 'symbol': return 0 + case 'undefined': return 0 + case 'object': return 0 + case 'function': return 0 + } + x; + ~~ +!!! error TS7027: Unreachable code detected. + } \ No newline at end of file diff --git a/tests/baselines/reference/unreachableSwitchTypeofUnknown.js b/tests/baselines/reference/unreachableSwitchTypeofUnknown.js new file mode 100644 index 0000000000000..3e476413ade04 --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofUnknown.js @@ -0,0 +1,29 @@ +//// [unreachable.ts] +const unreachable = (x: unknown): number => { + switch (typeof x) { + case 'string': return 0 + case 'number': return 0 + case 'bigint': return 0 + case 'boolean': return 0 + case 'symbol': return 0 + case 'undefined': return 0 + case 'object': return 0 + case 'function': return 0 + } + x; +} + +//// [unreachable.js] +var unreachable = function (x) { + switch (typeof x) { + case 'string': return 0; + case 'number': return 0; + case 'bigint': return 0; + case 'boolean': return 0; + case 'symbol': return 0; + case 'undefined': return 0; + case 'object': return 0; + case 'function': return 0; + } + x; +}; diff --git a/tests/baselines/reference/unreachableSwitchTypeofUnknown.symbols b/tests/baselines/reference/unreachableSwitchTypeofUnknown.symbols new file mode 100644 index 0000000000000..984583c2163cb --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofUnknown.symbols @@ -0,0 +1,20 @@ +=== tests/cases/compiler/unreachable.ts === +const unreachable = (x: unknown): number => { +>unreachable : Symbol(unreachable, Decl(unreachable.ts, 0, 5)) +>x : Symbol(x, Decl(unreachable.ts, 0, 21)) + + switch (typeof x) { +>x : Symbol(x, Decl(unreachable.ts, 0, 21)) + + case 'string': return 0 + case 'number': return 0 + case 'bigint': return 0 + case 'boolean': return 0 + case 'symbol': return 0 + case 'undefined': return 0 + case 'object': return 0 + case 'function': return 0 + } + x; +>x : Symbol(x, Decl(unreachable.ts, 0, 21)) +} diff --git a/tests/baselines/reference/unreachableSwitchTypeofUnknown.types b/tests/baselines/reference/unreachableSwitchTypeofUnknown.types new file mode 100644 index 0000000000000..55d77b66196fa --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofUnknown.types @@ -0,0 +1,45 @@ +=== tests/cases/compiler/unreachable.ts === +const unreachable = (x: unknown): number => { +>unreachable : (x: unknown) => number +>(x: unknown): number => { switch (typeof x) { case 'string': return 0 case 'number': return 0 case 'bigint': return 0 case 'boolean': return 0 case 'symbol': return 0 case 'undefined': return 0 case 'object': return 0 case 'function': return 0 } x;} : (x: unknown) => number +>x : unknown + + switch (typeof x) { +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : unknown + + case 'string': return 0 +>'string' : "string" +>0 : 0 + + case 'number': return 0 +>'number' : "number" +>0 : 0 + + case 'bigint': return 0 +>'bigint' : "bigint" +>0 : 0 + + case 'boolean': return 0 +>'boolean' : "boolean" +>0 : 0 + + case 'symbol': return 0 +>'symbol' : "symbol" +>0 : 0 + + case 'undefined': return 0 +>'undefined' : "undefined" +>0 : 0 + + case 'object': return 0 +>'object' : "object" +>0 : 0 + + case 'function': return 0 +>'function' : "function" +>0 : 0 + } + x; +>x : unknown +} diff --git a/tests/cases/compiler/unreachableSwitchTypeofAny.ts b/tests/cases/compiler/unreachableSwitchTypeofAny.ts new file mode 100644 index 0000000000000..e230434941b5c --- /dev/null +++ b/tests/cases/compiler/unreachableSwitchTypeofAny.ts @@ -0,0 +1,15 @@ +// @Filename: unreachable.ts +// @allowUnreachableCode: false +const unreachable = (x: any): number => { + switch (typeof x) { + case 'string': return 0 + case 'number': return 0 + case 'bigint': return 0 + case 'boolean': return 0 + case 'symbol': return 0 + case 'undefined': return 0 + case 'object': return 0 + case 'function': return 0 + } + x; +} \ No newline at end of file diff --git a/tests/cases/compiler/unreachableSwitchTypeofUnknown.ts b/tests/cases/compiler/unreachableSwitchTypeofUnknown.ts new file mode 100644 index 0000000000000..a2ec241bbdbc2 --- /dev/null +++ b/tests/cases/compiler/unreachableSwitchTypeofUnknown.ts @@ -0,0 +1,15 @@ +// @Filename: unreachable.ts +// @allowUnreachableCode: false +const unreachable = (x: unknown): number => { + switch (typeof x) { + case 'string': return 0 + case 'number': return 0 + case 'bigint': return 0 + case 'boolean': return 0 + case 'symbol': return 0 + case 'undefined': return 0 + case 'object': return 0 + case 'function': return 0 + } + x; +} \ No newline at end of file