mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 07:45:18 -06:00
Narrow by clause expressions in switches with true condition (#53681)
This commit is contained in:
parent
686cb1b63c
commit
70b7de11fb
@ -1680,7 +1680,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
|
||||
|
||||
function bindCaseBlock(node: CaseBlock): void {
|
||||
const clauses = node.clauses;
|
||||
const isNarrowingSwitch = isNarrowingExpression(node.parent.expression);
|
||||
const isNarrowingSwitch = node.parent.expression.kind === SyntaxKind.TrueKeyword || isNarrowingExpression(node.parent.expression);
|
||||
let fallthroughFlow = unreachableFlow;
|
||||
for (let i = 0; i < clauses.length; i++) {
|
||||
const clauseStart = i;
|
||||
|
||||
@ -27342,6 +27342,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
else if (expr.kind === SyntaxKind.TypeOfExpression && isMatchingReference(reference, (expr as TypeOfExpression).expression)) {
|
||||
type = narrowTypeBySwitchOnTypeOf(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
|
||||
}
|
||||
else if (expr.kind === SyntaxKind.TrueKeyword) {
|
||||
const clause = flow.switchStatement.caseBlock.clauses.find((_, index) => index === flow.clauseStart);
|
||||
const clauseExpression = clause && clause.kind === SyntaxKind.CaseClause ? clause.expression : undefined;
|
||||
if (clauseExpression) {
|
||||
type = narrowType(type, clauseExpression, /*assumeTrue*/ true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (strictNullChecks) {
|
||||
if (optionalChainContainsReference(expr, reference)) {
|
||||
|
||||
@ -0,0 +1,144 @@
|
||||
//// [tests/cases/compiler/narrowByClauseExpressionInSwitchTrue.ts] ////
|
||||
|
||||
=== narrowByClauseExpressionInSwitchTrue.ts ===
|
||||
// https://github.com/microsoft/TypeScript/issues/37178
|
||||
|
||||
type A = { type: "A" };
|
||||
>A : Symbol(A, Decl(narrowByClauseExpressionInSwitchTrue.ts, 0, 0))
|
||||
>type : Symbol(type, Decl(narrowByClauseExpressionInSwitchTrue.ts, 2, 10))
|
||||
|
||||
type B = { type: "B" };
|
||||
>B : Symbol(B, Decl(narrowByClauseExpressionInSwitchTrue.ts, 2, 23))
|
||||
>type : Symbol(type, Decl(narrowByClauseExpressionInSwitchTrue.ts, 3, 10))
|
||||
|
||||
type AorB = A | B;
|
||||
>AorB : Symbol(AorB, Decl(narrowByClauseExpressionInSwitchTrue.ts, 3, 23))
|
||||
>A : Symbol(A, Decl(narrowByClauseExpressionInSwitchTrue.ts, 0, 0))
|
||||
>B : Symbol(B, Decl(narrowByClauseExpressionInSwitchTrue.ts, 2, 23))
|
||||
|
||||
const isA = (x: AorB): x is A => x.type === "A";
|
||||
>isA : Symbol(isA, Decl(narrowByClauseExpressionInSwitchTrue.ts, 6, 5))
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 6, 13))
|
||||
>AorB : Symbol(AorB, Decl(narrowByClauseExpressionInSwitchTrue.ts, 3, 23))
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 6, 13))
|
||||
>A : Symbol(A, Decl(narrowByClauseExpressionInSwitchTrue.ts, 0, 0))
|
||||
>x.type : Symbol(type, Decl(narrowByClauseExpressionInSwitchTrue.ts, 2, 10), Decl(narrowByClauseExpressionInSwitchTrue.ts, 3, 10))
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 6, 13))
|
||||
>type : Symbol(type, Decl(narrowByClauseExpressionInSwitchTrue.ts, 2, 10), Decl(narrowByClauseExpressionInSwitchTrue.ts, 3, 10))
|
||||
|
||||
const isB = (x: AorB): x is B => x.type === "B";
|
||||
>isB : Symbol(isB, Decl(narrowByClauseExpressionInSwitchTrue.ts, 7, 5))
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 7, 13))
|
||||
>AorB : Symbol(AorB, Decl(narrowByClauseExpressionInSwitchTrue.ts, 3, 23))
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 7, 13))
|
||||
>B : Symbol(B, Decl(narrowByClauseExpressionInSwitchTrue.ts, 2, 23))
|
||||
>x.type : Symbol(type, Decl(narrowByClauseExpressionInSwitchTrue.ts, 2, 10), Decl(narrowByClauseExpressionInSwitchTrue.ts, 3, 10))
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 7, 13))
|
||||
>type : Symbol(type, Decl(narrowByClauseExpressionInSwitchTrue.ts, 2, 10), Decl(narrowByClauseExpressionInSwitchTrue.ts, 3, 10))
|
||||
|
||||
function test1(x: AorB) {
|
||||
>test1 : Symbol(test1, Decl(narrowByClauseExpressionInSwitchTrue.ts, 7, 48))
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 9, 15))
|
||||
>AorB : Symbol(AorB, Decl(narrowByClauseExpressionInSwitchTrue.ts, 3, 23))
|
||||
|
||||
switch (true) {
|
||||
case isA(x):
|
||||
>isA : Symbol(isA, Decl(narrowByClauseExpressionInSwitchTrue.ts, 6, 5))
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 9, 15))
|
||||
|
||||
x;
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 9, 15))
|
||||
|
||||
break;
|
||||
case isB(x):
|
||||
>isB : Symbol(isB, Decl(narrowByClauseExpressionInSwitchTrue.ts, 7, 5))
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 9, 15))
|
||||
|
||||
x;
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 9, 15))
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function test2(x: AorB) {
|
||||
>test2 : Symbol(test2, Decl(narrowByClauseExpressionInSwitchTrue.ts, 18, 1))
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 20, 15))
|
||||
>AorB : Symbol(AorB, Decl(narrowByClauseExpressionInSwitchTrue.ts, 3, 23))
|
||||
|
||||
switch (true) {
|
||||
case isA(x):
|
||||
>isA : Symbol(isA, Decl(narrowByClauseExpressionInSwitchTrue.ts, 6, 5))
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 20, 15))
|
||||
|
||||
x;
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 20, 15))
|
||||
|
||||
// fallthrough
|
||||
case isB(x):
|
||||
>isB : Symbol(isB, Decl(narrowByClauseExpressionInSwitchTrue.ts, 7, 5))
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 20, 15))
|
||||
|
||||
x;
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 20, 15))
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let x: string | undefined;
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 31, 3))
|
||||
|
||||
switch (true) {
|
||||
case typeof x !== "undefined":
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 31, 3))
|
||||
|
||||
x.trim();
|
||||
>x.trim : Symbol(String.trim, Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 31, 3))
|
||||
>trim : Symbol(String.trim, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
|
||||
type SomeType = { type: "SomeType" };
|
||||
>SomeType : Symbol(SomeType, Decl(narrowByClauseExpressionInSwitchTrue.ts, 36, 1))
|
||||
>type : Symbol(type, Decl(narrowByClauseExpressionInSwitchTrue.ts, 38, 17))
|
||||
|
||||
declare function isSomeType(x: unknown): x is SomeType;
|
||||
>isSomeType : Symbol(isSomeType, Decl(narrowByClauseExpressionInSwitchTrue.ts, 38, 37))
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 39, 28))
|
||||
>x : Symbol(x, Decl(narrowByClauseExpressionInSwitchTrue.ts, 39, 28))
|
||||
>SomeType : Symbol(SomeType, Decl(narrowByClauseExpressionInSwitchTrue.ts, 36, 1))
|
||||
|
||||
function processInput(input: string | RegExp | SomeType) {
|
||||
>processInput : Symbol(processInput, Decl(narrowByClauseExpressionInSwitchTrue.ts, 39, 55))
|
||||
>input : Symbol(input, Decl(narrowByClauseExpressionInSwitchTrue.ts, 41, 22))
|
||||
>RegExp : Symbol(RegExp, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>SomeType : Symbol(SomeType, Decl(narrowByClauseExpressionInSwitchTrue.ts, 36, 1))
|
||||
|
||||
switch (true) {
|
||||
case typeof input === "string":
|
||||
>input : Symbol(input, Decl(narrowByClauseExpressionInSwitchTrue.ts, 41, 22))
|
||||
|
||||
input;
|
||||
>input : Symbol(input, Decl(narrowByClauseExpressionInSwitchTrue.ts, 41, 22))
|
||||
|
||||
break;
|
||||
case input instanceof RegExp:
|
||||
>input : Symbol(input, Decl(narrowByClauseExpressionInSwitchTrue.ts, 41, 22))
|
||||
>RegExp : Symbol(RegExp, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
input;
|
||||
>input : Symbol(input, Decl(narrowByClauseExpressionInSwitchTrue.ts, 41, 22))
|
||||
|
||||
break;
|
||||
case isSomeType(input):
|
||||
>isSomeType : Symbol(isSomeType, Decl(narrowByClauseExpressionInSwitchTrue.ts, 38, 37))
|
||||
>input : Symbol(input, Decl(narrowByClauseExpressionInSwitchTrue.ts, 41, 22))
|
||||
|
||||
input;
|
||||
>input : Symbol(input, Decl(narrowByClauseExpressionInSwitchTrue.ts, 41, 22))
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,157 @@
|
||||
//// [tests/cases/compiler/narrowByClauseExpressionInSwitchTrue.ts] ////
|
||||
|
||||
=== narrowByClauseExpressionInSwitchTrue.ts ===
|
||||
// https://github.com/microsoft/TypeScript/issues/37178
|
||||
|
||||
type A = { type: "A" };
|
||||
>A : { type: "A"; }
|
||||
>type : "A"
|
||||
|
||||
type B = { type: "B" };
|
||||
>B : { type: "B"; }
|
||||
>type : "B"
|
||||
|
||||
type AorB = A | B;
|
||||
>AorB : A | B
|
||||
|
||||
const isA = (x: AorB): x is A => x.type === "A";
|
||||
>isA : (x: AorB) => x is A
|
||||
>(x: AorB): x is A => x.type === "A" : (x: AorB) => x is A
|
||||
>x : AorB
|
||||
>x.type === "A" : boolean
|
||||
>x.type : "A" | "B"
|
||||
>x : AorB
|
||||
>type : "A" | "B"
|
||||
>"A" : "A"
|
||||
|
||||
const isB = (x: AorB): x is B => x.type === "B";
|
||||
>isB : (x: AorB) => x is B
|
||||
>(x: AorB): x is B => x.type === "B" : (x: AorB) => x is B
|
||||
>x : AorB
|
||||
>x.type === "B" : boolean
|
||||
>x.type : "A" | "B"
|
||||
>x : AorB
|
||||
>type : "A" | "B"
|
||||
>"B" : "B"
|
||||
|
||||
function test1(x: AorB) {
|
||||
>test1 : (x: AorB) => void
|
||||
>x : AorB
|
||||
|
||||
switch (true) {
|
||||
>true : true
|
||||
|
||||
case isA(x):
|
||||
>isA(x) : boolean
|
||||
>isA : (x: AorB) => x is A
|
||||
>x : AorB
|
||||
|
||||
x;
|
||||
>x : A
|
||||
|
||||
break;
|
||||
case isB(x):
|
||||
>isB(x) : boolean
|
||||
>isB : (x: AorB) => x is B
|
||||
>x : AorB
|
||||
|
||||
x;
|
||||
>x : B
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function test2(x: AorB) {
|
||||
>test2 : (x: AorB) => void
|
||||
>x : AorB
|
||||
|
||||
switch (true) {
|
||||
>true : true
|
||||
|
||||
case isA(x):
|
||||
>isA(x) : boolean
|
||||
>isA : (x: AorB) => x is A
|
||||
>x : AorB
|
||||
|
||||
x;
|
||||
>x : A
|
||||
|
||||
// fallthrough
|
||||
case isB(x):
|
||||
>isB(x) : boolean
|
||||
>isB : (x: AorB) => x is B
|
||||
>x : AorB
|
||||
|
||||
x;
|
||||
>x : AorB
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let x: string | undefined;
|
||||
>x : string | undefined
|
||||
|
||||
switch (true) {
|
||||
>true : true
|
||||
|
||||
case typeof x !== "undefined":
|
||||
>typeof x !== "undefined" : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : string | undefined
|
||||
>"undefined" : "undefined"
|
||||
|
||||
x.trim();
|
||||
>x.trim() : string
|
||||
>x.trim : () => string
|
||||
>x : string
|
||||
>trim : () => string
|
||||
}
|
||||
|
||||
type SomeType = { type: "SomeType" };
|
||||
>SomeType : { type: "SomeType"; }
|
||||
>type : "SomeType"
|
||||
|
||||
declare function isSomeType(x: unknown): x is SomeType;
|
||||
>isSomeType : (x: unknown) => x is SomeType
|
||||
>x : unknown
|
||||
|
||||
function processInput(input: string | RegExp | SomeType) {
|
||||
>processInput : (input: string | RegExp | SomeType) => void
|
||||
>input : string | RegExp | SomeType
|
||||
|
||||
switch (true) {
|
||||
>true : true
|
||||
|
||||
case typeof input === "string":
|
||||
>typeof input === "string" : boolean
|
||||
>typeof input : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>input : string | RegExp | SomeType
|
||||
>"string" : "string"
|
||||
|
||||
input;
|
||||
>input : string
|
||||
|
||||
break;
|
||||
case input instanceof RegExp:
|
||||
>input instanceof RegExp : boolean
|
||||
>input : string | RegExp | SomeType
|
||||
>RegExp : RegExpConstructor
|
||||
|
||||
input;
|
||||
>input : RegExp
|
||||
|
||||
break;
|
||||
case isSomeType(input):
|
||||
>isSomeType(input) : boolean
|
||||
>isSomeType : (x: unknown) => x is SomeType
|
||||
>input : string | RegExp | SomeType
|
||||
|
||||
input;
|
||||
>input : SomeType
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
57
tests/cases/compiler/narrowByClauseExpressionInSwitchTrue.ts
Normal file
57
tests/cases/compiler/narrowByClauseExpressionInSwitchTrue.ts
Normal file
@ -0,0 +1,57 @@
|
||||
// @strict: true
|
||||
// @noEmit: true
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/37178
|
||||
|
||||
type A = { type: "A" };
|
||||
type B = { type: "B" };
|
||||
type AorB = A | B;
|
||||
|
||||
const isA = (x: AorB): x is A => x.type === "A";
|
||||
const isB = (x: AorB): x is B => x.type === "B";
|
||||
|
||||
function test1(x: AorB) {
|
||||
switch (true) {
|
||||
case isA(x):
|
||||
x;
|
||||
break;
|
||||
case isB(x):
|
||||
x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function test2(x: AorB) {
|
||||
switch (true) {
|
||||
case isA(x):
|
||||
x;
|
||||
// fallthrough
|
||||
case isB(x):
|
||||
x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let x: string | undefined;
|
||||
|
||||
switch (true) {
|
||||
case typeof x !== "undefined":
|
||||
x.trim();
|
||||
}
|
||||
|
||||
type SomeType = { type: "SomeType" };
|
||||
declare function isSomeType(x: unknown): x is SomeType;
|
||||
|
||||
function processInput(input: string | RegExp | SomeType) {
|
||||
switch (true) {
|
||||
case typeof input === "string":
|
||||
input;
|
||||
break;
|
||||
case input instanceof RegExp:
|
||||
input;
|
||||
break;
|
||||
case isSomeType(input):
|
||||
input;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user