diff --git a/internal/checker/flow.go b/internal/checker/flow.go index 84c0d81e8f..ef52e6c347 100644 --- a/internal/checker/flow.go +++ b/internal/checker/flow.go @@ -1967,19 +1967,19 @@ func (c *Checker) getSwitchClauseTypeOfWitnesses(node *ast.Node) []string { links := c.switchStatementLinks.Get(node) if !links.witnessesComputed { clauses := node.AsSwitchStatement().CaseBlock.AsCaseBlock().Clauses.Nodes - witnesses := make([]string, len(clauses)) - for i, clause := range clauses { - if clause.Kind == ast.KindCaseClause { - var text string - if ast.IsStringLiteralLike(clause.Expression()) { - text = clause.Expression().Text() - } - if text == "" { - witnesses = nil - break - } - if !slices.Contains(witnesses, text) { - witnesses[i] = text + // Return nil if one or more case clause expressions are not string literals. + hasNonStringLiteral := core.Some(clauses, func(clause *ast.Node) bool { + return clause.Kind == ast.KindCaseClause && !ast.IsStringLiteralLike(clause.Expression()) + }) + var witnesses []string + if !hasNonStringLiteral { + witnesses = make([]string, len(clauses)) + for i, clause := range clauses { + if clause.Kind == ast.KindCaseClause { + text := clause.Expression().Text() + if text != "" && !slices.Contains(witnesses, text) { + witnesses[i] = text + } } } } diff --git a/testdata/baselines/reference/compiler/typeofSwitchEmptyStringCase.errors.txt b/testdata/baselines/reference/compiler/typeofSwitchEmptyStringCase.errors.txt new file mode 100644 index 0000000000..6c047fc3e6 --- /dev/null +++ b/testdata/baselines/reference/compiler/typeofSwitchEmptyStringCase.errors.txt @@ -0,0 +1,15 @@ +typeofSwitchEmptyStringCase.ts(3,10): error TS2678: Type '""' is not comparable to type '"bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined"'. + + +==== typeofSwitchEmptyStringCase.ts (1 errors) ==== + function f(x: string | number) { + switch (typeof x) { + case "": + ~~ +!!! error TS2678: Type '""' is not comparable to type '"bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined"'. + case "string": + x.charAt(0); + break; + } + } + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/typeofSwitchEmptyStringCase.js b/testdata/baselines/reference/compiler/typeofSwitchEmptyStringCase.js new file mode 100644 index 0000000000..41e3b00924 --- /dev/null +++ b/testdata/baselines/reference/compiler/typeofSwitchEmptyStringCase.js @@ -0,0 +1,23 @@ +//// [tests/cases/compiler/typeofSwitchEmptyStringCase.ts] //// + +//// [typeofSwitchEmptyStringCase.ts] +function f(x: string | number) { + switch (typeof x) { + case "": + case "string": + x.charAt(0); + break; + } +} + + +//// [typeofSwitchEmptyStringCase.js] +"use strict"; +function f(x) { + switch (typeof x) { + case "": + case "string": + x.charAt(0); + break; + } +} diff --git a/testdata/baselines/reference/compiler/typeofSwitchEmptyStringCase.symbols b/testdata/baselines/reference/compiler/typeofSwitchEmptyStringCase.symbols new file mode 100644 index 0000000000..f6ed238e59 --- /dev/null +++ b/testdata/baselines/reference/compiler/typeofSwitchEmptyStringCase.symbols @@ -0,0 +1,21 @@ +//// [tests/cases/compiler/typeofSwitchEmptyStringCase.ts] //// + +=== typeofSwitchEmptyStringCase.ts === +function f(x: string | number) { +>f : Symbol(f, Decl(typeofSwitchEmptyStringCase.ts, 0, 0)) +>x : Symbol(x, Decl(typeofSwitchEmptyStringCase.ts, 0, 11)) + + switch (typeof x) { +>x : Symbol(x, Decl(typeofSwitchEmptyStringCase.ts, 0, 11)) + + case "": + case "string": + x.charAt(0); +>x.charAt : Symbol(String.charAt, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(typeofSwitchEmptyStringCase.ts, 0, 11)) +>charAt : Symbol(String.charAt, Decl(lib.es5.d.ts, --, --)) + + break; + } +} + diff --git a/testdata/baselines/reference/compiler/typeofSwitchEmptyStringCase.types b/testdata/baselines/reference/compiler/typeofSwitchEmptyStringCase.types new file mode 100644 index 0000000000..90534606d6 --- /dev/null +++ b/testdata/baselines/reference/compiler/typeofSwitchEmptyStringCase.types @@ -0,0 +1,28 @@ +//// [tests/cases/compiler/typeofSwitchEmptyStringCase.ts] //// + +=== typeofSwitchEmptyStringCase.ts === +function f(x: string | number) { +>f : (x: string | number) => void +>x : string | number + + switch (typeof x) { +>typeof x : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>x : string | number + + case "": +>"" : "" + + case "string": +>"string" : "string" + + x.charAt(0); +>x.charAt(0) : string +>x.charAt : (pos: number) => string +>x : string +>charAt : (pos: number) => string +>0 : 0 + + break; + } +} + diff --git a/testdata/tests/cases/compiler/typeofSwitchEmptyStringCase.ts b/testdata/tests/cases/compiler/typeofSwitchEmptyStringCase.ts new file mode 100644 index 0000000000..3b86a94a89 --- /dev/null +++ b/testdata/tests/cases/compiler/typeofSwitchEmptyStringCase.ts @@ -0,0 +1,10 @@ +// @strict: true + +function f(x: string | number) { + switch (typeof x) { + case "": + case "string": + x.charAt(0); + break; + } +}