From ad4542e79eabadd25e0add133dadbe90e0f0cfcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 5 Jun 2025 20:00:27 +0200 Subject: [PATCH] Port "Check if switch statements are exhaustive when their expressions is generic with a literal type constraint" --- internal/checker/flow.go | 2 +- ...exhaustiveSwitchStatementsGeneric1.symbols | 48 +++++++++++++++++++ .../exhaustiveSwitchStatementsGeneric1.types | 44 +++++++++++++++++ .../exhaustiveSwitchStatementsGeneric1.ts | 26 ++++++++++ 4 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 testdata/baselines/reference/compiler/exhaustiveSwitchStatementsGeneric1.symbols create mode 100644 testdata/baselines/reference/compiler/exhaustiveSwitchStatementsGeneric1.types create mode 100644 testdata/tests/cases/compiler/exhaustiveSwitchStatementsGeneric1.ts diff --git a/internal/checker/flow.go b/internal/checker/flow.go index d43a0ddccc..b8e1841171 100644 --- a/internal/checker/flow.go +++ b/internal/checker/flow.go @@ -1929,7 +1929,7 @@ func (c *Checker) computeExhaustiveSwitchStatement(node *ast.Node) bool { return c.getTypeFacts(t, notEqualFacts) == notEqualFacts }) } - t := c.checkExpressionCached(node.Expression()) + t := c.getBaseConstraintOrType(c.checkExpressionCached(node.Expression())) if !isLiteralType(t) { return false } diff --git a/testdata/baselines/reference/compiler/exhaustiveSwitchStatementsGeneric1.symbols b/testdata/baselines/reference/compiler/exhaustiveSwitchStatementsGeneric1.symbols new file mode 100644 index 0000000000..136cbc0e93 --- /dev/null +++ b/testdata/baselines/reference/compiler/exhaustiveSwitchStatementsGeneric1.symbols @@ -0,0 +1,48 @@ +//// [tests/cases/compiler/exhaustiveSwitchStatementsGeneric1.ts] //// + +=== exhaustiveSwitchStatementsGeneric1.ts === +// https://github.com/microsoft/typescript-go/issues/986 + +interface A { +>A : Symbol(A, Decl(exhaustiveSwitchStatementsGeneric1.ts, 0, 0)) + + type: "a"; +>type : Symbol(type, Decl(exhaustiveSwitchStatementsGeneric1.ts, 2, 13)) +} + +interface B { +>B : Symbol(B, Decl(exhaustiveSwitchStatementsGeneric1.ts, 4, 1)) + + type: "b"; +>type : Symbol(type, Decl(exhaustiveSwitchStatementsGeneric1.ts, 6, 13)) +} + +interface Types { +>Types : Symbol(Types, Decl(exhaustiveSwitchStatementsGeneric1.ts, 8, 1)) + + a: A; +>a : Symbol(a, Decl(exhaustiveSwitchStatementsGeneric1.ts, 10, 17)) +>A : Symbol(A, Decl(exhaustiveSwitchStatementsGeneric1.ts, 0, 0)) + + b: B; +>b : Symbol(b, Decl(exhaustiveSwitchStatementsGeneric1.ts, 11, 7)) +>B : Symbol(B, Decl(exhaustiveSwitchStatementsGeneric1.ts, 4, 1)) +} + +export function exhaustiveSwitch(type: T): boolean { +>exhaustiveSwitch : Symbol(exhaustiveSwitch, Decl(exhaustiveSwitchStatementsGeneric1.ts, 13, 1)) +>T : Symbol(T, Decl(exhaustiveSwitchStatementsGeneric1.ts, 15, 33)) +>Types : Symbol(Types, Decl(exhaustiveSwitchStatementsGeneric1.ts, 8, 1)) +>type : Symbol(type, Decl(exhaustiveSwitchStatementsGeneric1.ts, 15, 56)) +>T : Symbol(T, Decl(exhaustiveSwitchStatementsGeneric1.ts, 15, 33)) + + switch (type) { +>type : Symbol(type, Decl(exhaustiveSwitchStatementsGeneric1.ts, 15, 56)) + + case "a": + return true; + case "b": + return true; + } +} + diff --git a/testdata/baselines/reference/compiler/exhaustiveSwitchStatementsGeneric1.types b/testdata/baselines/reference/compiler/exhaustiveSwitchStatementsGeneric1.types new file mode 100644 index 0000000000..5cce40cc85 --- /dev/null +++ b/testdata/baselines/reference/compiler/exhaustiveSwitchStatementsGeneric1.types @@ -0,0 +1,44 @@ +//// [tests/cases/compiler/exhaustiveSwitchStatementsGeneric1.ts] //// + +=== exhaustiveSwitchStatementsGeneric1.ts === +// https://github.com/microsoft/typescript-go/issues/986 + +interface A { + type: "a"; +>type : "a" +} + +interface B { + type: "b"; +>type : "b" +} + +interface Types { + a: A; +>a : A + + b: B; +>b : B +} + +export function exhaustiveSwitch(type: T): boolean { +>exhaustiveSwitch : (type: T) => boolean +>type : T + + switch (type) { +>type : T + + case "a": +>"a" : "a" + + return true; +>true : true + + case "b": +>"b" : "b" + + return true; +>true : true + } +} + diff --git a/testdata/tests/cases/compiler/exhaustiveSwitchStatementsGeneric1.ts b/testdata/tests/cases/compiler/exhaustiveSwitchStatementsGeneric1.ts new file mode 100644 index 0000000000..0b5dcb762f --- /dev/null +++ b/testdata/tests/cases/compiler/exhaustiveSwitchStatementsGeneric1.ts @@ -0,0 +1,26 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/typescript-go/issues/986 + +interface A { + type: "a"; +} + +interface B { + type: "b"; +} + +interface Types { + a: A; + b: B; +} + +export function exhaustiveSwitch(type: T): boolean { + switch (type) { + case "a": + return true; + case "b": + return true; + } +}