Skip to content

Commit 06c9a28

Browse files
Kingwlmhegazy
authored andcommitted
fix narrowType check with template literals (microsoft#21024)
1 parent 86eab34 commit 06c9a28

6 files changed

Lines changed: 99 additions & 5 deletions

File tree

src/compiler/binder.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -746,11 +746,11 @@ namespace ts {
746746
}
747747

748748
function isNarrowingTypeofOperands(expr1: Expression, expr2: Expression) {
749-
return expr1.kind === SyntaxKind.TypeOfExpression && isNarrowableOperand((<TypeOfExpression>expr1).expression) && expr2.kind === SyntaxKind.StringLiteral;
749+
return expr1.kind === SyntaxKind.TypeOfExpression && isNarrowableOperand((<TypeOfExpression>expr1).expression) && (expr2.kind === SyntaxKind.StringLiteral || expr2.kind === SyntaxKind.NoSubstitutionTemplateLiteral);
750750
}
751751

752752
function isNarrowableInOperands(left: Expression, right: Expression) {
753-
return left.kind === SyntaxKind.StringLiteral && isNarrowingExpression(right);
753+
return (left.kind === SyntaxKind.StringLiteral || left.kind === SyntaxKind.NoSubstitutionTemplateLiteral) && isNarrowingExpression(right);
754754
}
755755

756756
function isNarrowingBinaryExpression(expr: BinaryExpression) {

src/compiler/checker.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12838,10 +12838,10 @@ namespace ts {
1283812838
const operator = expr.operatorToken.kind;
1283912839
const left = getReferenceCandidate(expr.left);
1284012840
const right = getReferenceCandidate(expr.right);
12841-
if (left.kind === SyntaxKind.TypeOfExpression && right.kind === SyntaxKind.StringLiteral) {
12841+
if (left.kind === SyntaxKind.TypeOfExpression && (right.kind === SyntaxKind.StringLiteral || right.kind === SyntaxKind.NoSubstitutionTemplateLiteral)) {
1284212842
return narrowTypeByTypeof(type, <TypeOfExpression>left, operator, <LiteralExpression>right, assumeTrue);
1284312843
}
12844-
if (right.kind === SyntaxKind.TypeOfExpression && left.kind === SyntaxKind.StringLiteral) {
12844+
if (right.kind === SyntaxKind.TypeOfExpression && (left.kind === SyntaxKind.StringLiteral || left.kind === SyntaxKind.NoSubstitutionTemplateLiteral)) {
1284512845
return narrowTypeByTypeof(type, <TypeOfExpression>right, operator, <LiteralExpression>left, assumeTrue);
1284612846
}
1284712847
if (isMatchingReference(reference, left)) {
@@ -12864,7 +12864,7 @@ namespace ts {
1286412864
return narrowTypeByInstanceof(type, expr, assumeTrue);
1286512865
case SyntaxKind.InKeyword:
1286612866
const target = getReferenceCandidate(expr.right);
12867-
if (expr.left.kind === SyntaxKind.StringLiteral && isMatchingReference(reference, target)) {
12867+
if ((expr.left.kind === SyntaxKind.StringLiteral || expr.left.kind === SyntaxKind.NoSubstitutionTemplateLiteral) && isMatchingReference(reference, target)) {
1286812868
return narrowByInKeyword(type, <LiteralExpression>expr.left, assumeTrue);
1286912869
}
1287012870
break;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//// [controlFlowWithTemplateLiterals.ts]
2+
declare const envVar: string | undefined;
3+
if (typeof envVar === `string`) {
4+
envVar.slice(0)
5+
}
6+
7+
declare const obj: {test: string} | {}
8+
if (`test` in obj) {
9+
obj.test.slice(0)
10+
}
11+
12+
13+
//// [controlFlowWithTemplateLiterals.js]
14+
if (typeof envVar === "string") {
15+
envVar.slice(0);
16+
}
17+
if ("test" in obj) {
18+
obj.test.slice(0);
19+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
=== tests/cases/conformance/controlFlow/controlFlowWithTemplateLiterals.ts ===
2+
declare const envVar: string | undefined;
3+
>envVar : Symbol(envVar, Decl(controlFlowWithTemplateLiterals.ts, 0, 13))
4+
5+
if (typeof envVar === `string`) {
6+
>envVar : Symbol(envVar, Decl(controlFlowWithTemplateLiterals.ts, 0, 13))
7+
8+
envVar.slice(0)
9+
>envVar.slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
10+
>envVar : Symbol(envVar, Decl(controlFlowWithTemplateLiterals.ts, 0, 13))
11+
>slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
12+
}
13+
14+
declare const obj: {test: string} | {}
15+
>obj : Symbol(obj, Decl(controlFlowWithTemplateLiterals.ts, 5, 13))
16+
>test : Symbol(test, Decl(controlFlowWithTemplateLiterals.ts, 5, 20))
17+
18+
if (`test` in obj) {
19+
>obj : Symbol(obj, Decl(controlFlowWithTemplateLiterals.ts, 5, 13))
20+
21+
obj.test.slice(0)
22+
>obj.test.slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
23+
>obj.test : Symbol(test, Decl(controlFlowWithTemplateLiterals.ts, 5, 20))
24+
>obj : Symbol(obj, Decl(controlFlowWithTemplateLiterals.ts, 5, 13))
25+
>test : Symbol(test, Decl(controlFlowWithTemplateLiterals.ts, 5, 20))
26+
>slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
27+
}
28+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
=== tests/cases/conformance/controlFlow/controlFlowWithTemplateLiterals.ts ===
2+
declare const envVar: string | undefined;
3+
>envVar : string | undefined
4+
5+
if (typeof envVar === `string`) {
6+
>typeof envVar === `string` : boolean
7+
>typeof envVar : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
8+
>envVar : string | undefined
9+
>`string` : "string"
10+
11+
envVar.slice(0)
12+
>envVar.slice(0) : string
13+
>envVar.slice : (start?: number | undefined, end?: number | undefined) => string
14+
>envVar : string
15+
>slice : (start?: number | undefined, end?: number | undefined) => string
16+
>0 : 0
17+
}
18+
19+
declare const obj: {test: string} | {}
20+
>obj : {} | { test: string; }
21+
>test : string
22+
23+
if (`test` in obj) {
24+
>`test` in obj : boolean
25+
>`test` : "test"
26+
>obj : {} | { test: string; }
27+
28+
obj.test.slice(0)
29+
>obj.test.slice(0) : string
30+
>obj.test.slice : (start?: number | undefined, end?: number | undefined) => string
31+
>obj.test : string
32+
>obj : { test: string; }
33+
>test : string
34+
>slice : (start?: number | undefined, end?: number | undefined) => string
35+
>0 : 0
36+
}
37+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// @strictNullChecks: true
2+
declare const envVar: string | undefined;
3+
if (typeof envVar === `string`) {
4+
envVar.slice(0)
5+
}
6+
7+
declare const obj: {test: string} | {}
8+
if (`test` in obj) {
9+
obj.test.slice(0)
10+
}

0 commit comments

Comments
 (0)