Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11336,6 +11336,10 @@ namespace ts {
return !!getPropertyOfType(type, "0" as __String);
}

function isNeitherUnitTypeNorNever(type: Type): boolean {
return !(type.flags & (TypeFlags.Unit | TypeFlags.Never));
}

function isUnitType(type: Type): boolean {
return !!(type.flags & TypeFlags.Unit);
}
Expand Down Expand Up @@ -12817,24 +12821,17 @@ namespace ts {

function getTypeOfSwitchClause(clause: CaseClause | DefaultClause) {
if (clause.kind === SyntaxKind.CaseClause) {
const caseType = getRegularTypeOfLiteralType(getTypeOfExpression(clause.expression));
return isUnitType(caseType) ? caseType : undefined;
return getRegularTypeOfLiteralType(getTypeOfExpression(clause.expression));
}
return neverType;
}

function getSwitchClauseTypes(switchStatement: SwitchStatement): Type[] {
const links = getNodeLinks(switchStatement);
if (!links.switchTypes) {
// If all case clauses specify expressions that have unit types, we return an array
// of those unit types. Otherwise we return an empty array.
links.switchTypes = [];
for (const clause of switchStatement.caseBlock.clauses) {
const type = getTypeOfSwitchClause(clause);
if (type === undefined) {
return links.switchTypes = emptyArray;
}
links.switchTypes.push(type);
links.switchTypes.push(getTypeOfSwitchClause(clause));
}
}
return links.switchTypes;
Expand Down Expand Up @@ -18953,7 +18950,7 @@ namespace ts {
return false;
}
const switchTypes = getSwitchClauseTypes(node);
if (!switchTypes.length) {
if (!switchTypes.length || some(switchTypes, isNeitherUnitTypeNorNever)) {
return false;
}
return eachTypeContainedIn(mapType(type, getRegularTypeOfLiteralType), switchTypes);
Expand Down
6 changes: 3 additions & 3 deletions tests/baselines/reference/literalTypes1.types
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,19 @@ function f2(x: 0 | 1 | 2) {
>zero : 0

x;
>x : 0 | 1 | 2
>x : 0

break;
case oneOrTwo:
>oneOrTwo : 1 | 2

x;
>x : 0 | 1 | 2
>x : 1 | 2

break;
default:
x;
>x : 0 | 1 | 2
>x : 1 | 2
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ switch (x) {
>"baz" : "baz"

x;
>x : "foo"
>x : never

y;
>y : "foo" | "bar"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//// [switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts]
export const narrowToLiterals = (str: string) => {
switch (str) {
case 'abc': {
// inferred type as `abc`
return str;
}
default:
return 'defaultValue';
}
};

export const narrowToString = (str: string, someOtherStr: string) => {
switch (str) {
case 'abc': {
// inferred type should be `abc`
return str;
}
case someOtherStr: {
// `string`
return str;
}
default:
return 'defaultValue';
}
};

export const narrowToStringOrNumber = (str: string | number, someNumber: number) => {
switch (str) {
case 'abc': {
// inferred type should be `abc`
return str;
}
case someNumber: {
// inferred type should be `number`
return str;
}
default:
return 'defaultValue';
}
};

//// [switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.js]
"use strict";
exports.__esModule = true;
exports.narrowToLiterals = function (str) {
switch (str) {
case 'abc': {
// inferred type as `abc`
return str;
}
default:
return 'defaultValue';
}
};
exports.narrowToString = function (str, someOtherStr) {
switch (str) {
case 'abc': {
// inferred type should be `abc`
return str;
}
case someOtherStr: {
// `string`
return str;
}
default:
return 'defaultValue';
}
};
exports.narrowToStringOrNumber = function (str, someNumber) {
switch (str) {
case 'abc': {
// inferred type should be `abc`
return str;
}
case someNumber: {
// inferred type should be `number`
return str;
}
default:
return 'defaultValue';
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
=== tests/cases/compiler/switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts ===
export const narrowToLiterals = (str: string) => {
>narrowToLiterals : Symbol(narrowToLiterals, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 0, 12))
>str : Symbol(str, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 0, 33))

switch (str) {
>str : Symbol(str, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 0, 33))

case 'abc': {
// inferred type as `abc`
return str;
>str : Symbol(str, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 0, 33))
}
default:
return 'defaultValue';
}
};

export const narrowToString = (str: string, someOtherStr: string) => {
>narrowToString : Symbol(narrowToString, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 11, 14))
>str : Symbol(str, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 11, 33))
>someOtherStr : Symbol(someOtherStr, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 11, 45))

switch (str) {
>str : Symbol(str, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 11, 33))

case 'abc': {
// inferred type should be `abc`
return str;
>str : Symbol(str, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 11, 33))
}
case someOtherStr: {
>someOtherStr : Symbol(someOtherStr, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 11, 45))

// `string`
return str;
>str : Symbol(str, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 11, 33))
}
default:
return 'defaultValue';
}
};

export const narrowToStringOrNumber = (str: string | number, someNumber: number) => {
>narrowToStringOrNumber : Symbol(narrowToStringOrNumber, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 26, 14))
>str : Symbol(str, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 26, 41))
>someNumber : Symbol(someNumber, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 26, 62))

switch (str) {
>str : Symbol(str, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 26, 41))

case 'abc': {
// inferred type should be `abc`
return str;
>str : Symbol(str, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 26, 41))
}
case someNumber: {
>someNumber : Symbol(someNumber, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 26, 62))

// inferred type should be `number`
return str;
>str : Symbol(str, Decl(switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts, 26, 41))
}
default:
return 'defaultValue';
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
=== tests/cases/compiler/switchCaseNarrowsMatchingClausesEvenWhenNonMatchingClausesExist.ts ===
export const narrowToLiterals = (str: string) => {
>narrowToLiterals : (str: string) => "abc" | "defaultValue"
>(str: string) => { switch (str) { case 'abc': { // inferred type as `abc` return str; } default: return 'defaultValue'; } } : (str: string) => "abc" | "defaultValue"
>str : string

switch (str) {
>str : string

case 'abc': {
>'abc' : "abc"

// inferred type as `abc`
return str;
>str : "abc"
}
default:
return 'defaultValue';
>'defaultValue' : "defaultValue"
}
};

export const narrowToString = (str: string, someOtherStr: string) => {
>narrowToString : (str: string, someOtherStr: string) => string
>(str: string, someOtherStr: string) => { switch (str) { case 'abc': { // inferred type should be `abc` return str; } case someOtherStr: { // `string` return str; } default: return 'defaultValue'; } } : (str: string, someOtherStr: string) => string
>str : string
>someOtherStr : string

switch (str) {
>str : string

case 'abc': {
>'abc' : "abc"

// inferred type should be `abc`
return str;
>str : "abc"
}
case someOtherStr: {
>someOtherStr : string

// `string`
return str;
>str : string
}
default:
return 'defaultValue';
>'defaultValue' : "defaultValue"
}
};

export const narrowToStringOrNumber = (str: string | number, someNumber: number) => {
>narrowToStringOrNumber : (str: string | number, someNumber: number) => number | "abc" | "defaultValue"
>(str: string | number, someNumber: number) => { switch (str) { case 'abc': { // inferred type should be `abc` return str; } case someNumber: { // inferred type should be `number` return str; } default: return 'defaultValue'; } } : (str: string | number, someNumber: number) => number | "abc" | "defaultValue"
>str : string | number
>someNumber : number

switch (str) {
>str : string | number

case 'abc': {
>'abc' : "abc"

// inferred type should be `abc`
return str;
>str : "abc"
}
case someNumber: {
>someNumber : number

// inferred type should be `number`
return str;
>str : number
}
default:
return 'defaultValue';
>'defaultValue' : "defaultValue"
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
export const narrowToLiterals = (str: string) => {
switch (str) {
case 'abc': {
// inferred type as `abc`
return str;
}
default:
return 'defaultValue';
}
};

export const narrowToString = (str: string, someOtherStr: string) => {
switch (str) {
case 'abc': {
// inferred type should be `abc`
return str;
}
case someOtherStr: {
// `string`
return str;
}
default:
return 'defaultValue';
}
};

export const narrowToStringOrNumber = (str: string | number, someNumber: number) => {
switch (str) {
case 'abc': {
// inferred type should be `abc`
return str;
}
case someNumber: {
// inferred type should be `number`
return str;
}
default:
return 'defaultValue';
}
};