diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 4fdfacdbf50..701b1bfb249 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -746,11 +746,11 @@ namespace ts { } function isNarrowingTypeofOperands(expr1: Expression, expr2: Expression) { - return expr1.kind === SyntaxKind.TypeOfExpression && isNarrowableOperand((expr1).expression) && expr2.kind === SyntaxKind.StringLiteral; + return expr1.kind === SyntaxKind.TypeOfExpression && isNarrowableOperand((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) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ff6371e679c..b04d39bb2b1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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, left, operator, 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, right, operator, 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, expr.left, assumeTrue); } break; diff --git a/tests/baselines/reference/controlFlowWithTemplateLiterals.js b/tests/baselines/reference/controlFlowWithTemplateLiterals.js new file mode 100644 index 00000000000..b640848f5ec --- /dev/null +++ b/tests/baselines/reference/controlFlowWithTemplateLiterals.js @@ -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); +} diff --git a/tests/baselines/reference/controlFlowWithTemplateLiterals.symbols b/tests/baselines/reference/controlFlowWithTemplateLiterals.symbols new file mode 100644 index 00000000000..978a0e507e0 --- /dev/null +++ b/tests/baselines/reference/controlFlowWithTemplateLiterals.symbols @@ -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, --, --)) +} + diff --git a/tests/baselines/reference/controlFlowWithTemplateLiterals.types b/tests/baselines/reference/controlFlowWithTemplateLiterals.types new file mode 100644 index 00000000000..7775d26fe38 --- /dev/null +++ b/tests/baselines/reference/controlFlowWithTemplateLiterals.types @@ -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 +} + diff --git a/tests/cases/conformance/controlFlow/controlFlowWithTemplateLiterals.ts b/tests/cases/conformance/controlFlow/controlFlowWithTemplateLiterals.ts new file mode 100644 index 00000000000..18777844558 --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowWithTemplateLiterals.ts @@ -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) +}