fix narrowType check with template literals (#21024)

This commit is contained in:
Wenlu Wang
2018-01-09 05:06:58 +08:00
committed by Mohamed Hegazy
parent 86eab34758
commit 06c9a28b36
6 changed files with 99 additions and 5 deletions

View File

@@ -746,11 +746,11 @@ namespace ts {
}
function isNarrowingTypeofOperands(expr1: Expression, expr2: Expression) {
return expr1.kind === SyntaxKind.TypeOfExpression && isNarrowableOperand((<TypeOfExpression>expr1).expression) && expr2.kind === SyntaxKind.StringLiteral;
return expr1.kind === SyntaxKind.TypeOfExpression && isNarrowableOperand((<TypeOfExpression>expr1).expression) && (expr2.kind === SyntaxKind.StringLiteral || expr2.kind === SyntaxKind.NoSubstitutionTemplateLiteral);
}
function isNarrowableInOperands(left: Expression, right: Expression) {
return left.kind === SyntaxKind.StringLiteral && isNarrowingExpression(right);
return (left.kind === SyntaxKind.StringLiteral || left.kind === SyntaxKind.NoSubstitutionTemplateLiteral) && isNarrowingExpression(right);
}
function isNarrowingBinaryExpression(expr: BinaryExpression) {

View File

@@ -12838,10 +12838,10 @@ namespace ts {
const operator = expr.operatorToken.kind;
const left = getReferenceCandidate(expr.left);
const right = getReferenceCandidate(expr.right);
if (left.kind === SyntaxKind.TypeOfExpression && right.kind === SyntaxKind.StringLiteral) {
if (left.kind === SyntaxKind.TypeOfExpression && (right.kind === SyntaxKind.StringLiteral || right.kind === SyntaxKind.NoSubstitutionTemplateLiteral)) {
return narrowTypeByTypeof(type, <TypeOfExpression>left, operator, <LiteralExpression>right, assumeTrue);
}
if (right.kind === SyntaxKind.TypeOfExpression && left.kind === SyntaxKind.StringLiteral) {
if (right.kind === SyntaxKind.TypeOfExpression && (left.kind === SyntaxKind.StringLiteral || left.kind === SyntaxKind.NoSubstitutionTemplateLiteral)) {
return narrowTypeByTypeof(type, <TypeOfExpression>right, operator, <LiteralExpression>left, assumeTrue);
}
if (isMatchingReference(reference, left)) {
@@ -12864,7 +12864,7 @@ namespace ts {
return narrowTypeByInstanceof(type, expr, assumeTrue);
case SyntaxKind.InKeyword:
const target = getReferenceCandidate(expr.right);
if (expr.left.kind === SyntaxKind.StringLiteral && isMatchingReference(reference, target)) {
if ((expr.left.kind === SyntaxKind.StringLiteral || expr.left.kind === SyntaxKind.NoSubstitutionTemplateLiteral) && isMatchingReference(reference, target)) {
return narrowByInKeyword(type, <LiteralExpression>expr.left, assumeTrue);
}
break;

View File

@@ -0,0 +1,19 @@
//// [controlFlowWithTemplateLiterals.ts]
declare const envVar: string | undefined;
if (typeof envVar === `string`) {
envVar.slice(0)
}
declare const obj: {test: string} | {}
if (`test` in obj) {
obj.test.slice(0)
}
//// [controlFlowWithTemplateLiterals.js]
if (typeof envVar === "string") {
envVar.slice(0);
}
if ("test" in obj) {
obj.test.slice(0);
}

View File

@@ -0,0 +1,28 @@
=== tests/cases/conformance/controlFlow/controlFlowWithTemplateLiterals.ts ===
declare const envVar: string | undefined;
>envVar : Symbol(envVar, Decl(controlFlowWithTemplateLiterals.ts, 0, 13))
if (typeof envVar === `string`) {
>envVar : Symbol(envVar, Decl(controlFlowWithTemplateLiterals.ts, 0, 13))
envVar.slice(0)
>envVar.slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
>envVar : Symbol(envVar, Decl(controlFlowWithTemplateLiterals.ts, 0, 13))
>slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
}
declare const obj: {test: string} | {}
>obj : Symbol(obj, Decl(controlFlowWithTemplateLiterals.ts, 5, 13))
>test : Symbol(test, Decl(controlFlowWithTemplateLiterals.ts, 5, 20))
if (`test` in obj) {
>obj : Symbol(obj, Decl(controlFlowWithTemplateLiterals.ts, 5, 13))
obj.test.slice(0)
>obj.test.slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
>obj.test : Symbol(test, Decl(controlFlowWithTemplateLiterals.ts, 5, 20))
>obj : Symbol(obj, Decl(controlFlowWithTemplateLiterals.ts, 5, 13))
>test : Symbol(test, Decl(controlFlowWithTemplateLiterals.ts, 5, 20))
>slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
}

View File

@@ -0,0 +1,37 @@
=== tests/cases/conformance/controlFlow/controlFlowWithTemplateLiterals.ts ===
declare const envVar: string | undefined;
>envVar : string | undefined
if (typeof envVar === `string`) {
>typeof envVar === `string` : boolean
>typeof envVar : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>envVar : string | undefined
>`string` : "string"
envVar.slice(0)
>envVar.slice(0) : string
>envVar.slice : (start?: number | undefined, end?: number | undefined) => string
>envVar : string
>slice : (start?: number | undefined, end?: number | undefined) => string
>0 : 0
}
declare const obj: {test: string} | {}
>obj : {} | { test: string; }
>test : string
if (`test` in obj) {
>`test` in obj : boolean
>`test` : "test"
>obj : {} | { test: string; }
obj.test.slice(0)
>obj.test.slice(0) : string
>obj.test.slice : (start?: number | undefined, end?: number | undefined) => string
>obj.test : string
>obj : { test: string; }
>test : string
>slice : (start?: number | undefined, end?: number | undefined) => string
>0 : 0
}

View File

@@ -0,0 +1,10 @@
// @strictNullChecks: true
declare const envVar: string | undefined;
if (typeof envVar === `string`) {
envVar.slice(0)
}
declare const obj: {test: string} | {}
if (`test` in obj) {
obj.test.slice(0)
}