Fixed type narrowing in switch statements with parenthesized expressions (#56035)

This commit is contained in:
Mateusz Burzyński 2023-10-09 20:36:16 +02:00 committed by GitHub
parent 61a96b1641
commit 9144836360
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 162 additions and 1 deletions

View File

@ -27446,7 +27446,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
function getTypeAtSwitchClause(flow: FlowSwitchClause): FlowType {
const expr = flow.switchStatement.expression;
const expr = skipParentheses(flow.switchStatement.expression);
const flowType = getTypeAtFlowNode(flow.antecedent);
let type = getTypeFromFlowType(flowType);
if (isMatchingReference(reference, expr)) {

View File

@ -0,0 +1,66 @@
//// [tests/cases/compiler/narrowByParenthesizedSwitchExpression.ts] ////
=== narrowByParenthesizedSwitchExpression.ts ===
interface Base {
>Base : Symbol(Base, Decl(narrowByParenthesizedSwitchExpression.ts, 0, 0))
type: "foo" | "bar";
>type : Symbol(Base.type, Decl(narrowByParenthesizedSwitchExpression.ts, 0, 16))
}
interface Foo extends Base {
>Foo : Symbol(Foo, Decl(narrowByParenthesizedSwitchExpression.ts, 2, 1))
>Base : Symbol(Base, Decl(narrowByParenthesizedSwitchExpression.ts, 0, 0))
type: "foo";
>type : Symbol(Foo.type, Decl(narrowByParenthesizedSwitchExpression.ts, 4, 28))
foo: string;
>foo : Symbol(Foo.foo, Decl(narrowByParenthesizedSwitchExpression.ts, 5, 14))
}
interface Bar extends Base {
>Bar : Symbol(Bar, Decl(narrowByParenthesizedSwitchExpression.ts, 7, 1))
>Base : Symbol(Base, Decl(narrowByParenthesizedSwitchExpression.ts, 0, 0))
type: "bar";
>type : Symbol(Bar.type, Decl(narrowByParenthesizedSwitchExpression.ts, 9, 28))
bar: number;
>bar : Symbol(Bar.bar, Decl(narrowByParenthesizedSwitchExpression.ts, 10, 14))
}
function getV(): Foo | Bar {
>getV : Symbol(getV, Decl(narrowByParenthesizedSwitchExpression.ts, 12, 1))
>Foo : Symbol(Foo, Decl(narrowByParenthesizedSwitchExpression.ts, 2, 1))
>Bar : Symbol(Bar, Decl(narrowByParenthesizedSwitchExpression.ts, 7, 1))
return null!;
}
const v = getV();
>v : Symbol(v, Decl(narrowByParenthesizedSwitchExpression.ts, 18, 5))
>getV : Symbol(getV, Decl(narrowByParenthesizedSwitchExpression.ts, 12, 1))
switch ((v.type)) {
>v.type : Symbol(type, Decl(narrowByParenthesizedSwitchExpression.ts, 4, 28), Decl(narrowByParenthesizedSwitchExpression.ts, 9, 28))
>v : Symbol(v, Decl(narrowByParenthesizedSwitchExpression.ts, 18, 5))
>type : Symbol(type, Decl(narrowByParenthesizedSwitchExpression.ts, 4, 28), Decl(narrowByParenthesizedSwitchExpression.ts, 9, 28))
case "bar":
v.bar;
>v.bar : Symbol(Bar.bar, Decl(narrowByParenthesizedSwitchExpression.ts, 10, 14))
>v : Symbol(v, Decl(narrowByParenthesizedSwitchExpression.ts, 18, 5))
>bar : Symbol(Bar.bar, Decl(narrowByParenthesizedSwitchExpression.ts, 10, 14))
break;
case "foo":
v.foo;
>v.foo : Symbol(Foo.foo, Decl(narrowByParenthesizedSwitchExpression.ts, 5, 14))
>v : Symbol(v, Decl(narrowByParenthesizedSwitchExpression.ts, 18, 5))
>foo : Symbol(Foo.foo, Decl(narrowByParenthesizedSwitchExpression.ts, 5, 14))
break;
}

View File

@ -0,0 +1,63 @@
//// [tests/cases/compiler/narrowByParenthesizedSwitchExpression.ts] ////
=== narrowByParenthesizedSwitchExpression.ts ===
interface Base {
type: "foo" | "bar";
>type : "foo" | "bar"
}
interface Foo extends Base {
type: "foo";
>type : "foo"
foo: string;
>foo : string
}
interface Bar extends Base {
type: "bar";
>type : "bar"
bar: number;
>bar : number
}
function getV(): Foo | Bar {
>getV : () => Foo | Bar
return null!;
>null! : never
}
const v = getV();
>v : Foo | Bar
>getV() : Foo | Bar
>getV : () => Foo | Bar
switch ((v.type)) {
>(v.type) : "foo" | "bar"
>v.type : "foo" | "bar"
>v : Foo | Bar
>type : "foo" | "bar"
case "bar":
>"bar" : "bar"
v.bar;
>v.bar : number
>v : Bar
>bar : number
break;
case "foo":
>"foo" : "foo"
v.foo;
>v.foo : string
>v : Foo
>foo : string
break;
}

View File

@ -0,0 +1,32 @@
// @strict: true
// @noEmit: true
interface Base {
type: "foo" | "bar";
}
interface Foo extends Base {
type: "foo";
foo: string;
}
interface Bar extends Base {
type: "bar";
bar: number;
}
function getV(): Foo | Bar {
return null!;
}
const v = getV();
switch ((v.type)) {
case "bar":
v.bar;
break;
case "foo":
v.foo;
break;
}