diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 4733221236c..946a567467d 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -882,11 +882,11 @@ namespace ts { } function isNarrowableReference(expr: Expression): boolean { - return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.PrivateIdentifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword || - (isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) || - isBinaryExpression(expr) && expr.operatorToken.kind === SyntaxKind.CommaToken && isNarrowableReference(expr.right) || - isElementAccessExpression(expr) && isStringOrNumericLiteralLike(expr.argumentExpression) && isNarrowableReference(expr.expression) || - isAssignmentExpression(expr) && isNarrowableReference(expr.left); + return isDottedName(expr) + || (isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) + || isBinaryExpression(expr) && expr.operatorToken.kind === SyntaxKind.CommaToken && isNarrowableReference(expr.right) + || isElementAccessExpression(expr) && isStringOrNumericLiteralLike(expr.argumentExpression) && isNarrowableReference(expr.expression) + || isAssignmentExpression(expr) && isNarrowableReference(expr.left); } function containsNarrowableReference(expr: Expression): boolean { @@ -1369,7 +1369,7 @@ namespace ts { // is potentially an assertion and is therefore included in the control flow. if (node.kind === SyntaxKind.CallExpression) { const call = node; - if (isDottedName(call.expression) && call.expression.kind !== SyntaxKind.SuperKeyword) { + if (call.expression.kind !== SyntaxKind.SuperKeyword && isDottedName(call.expression)) { currentFlow = createFlowCall(currentFlow, call); } } @@ -2542,6 +2542,7 @@ namespace ts { node.flowNode = currentFlow; } break; + case SyntaxKind.MetaProperty: case SyntaxKind.SuperKeyword: node.flowNode = currentFlow; break; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bfd16e297c0..8bcd277cd1e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21320,6 +21320,12 @@ namespace ts { (isBinaryExpression(target) && target.operatorToken.kind === SyntaxKind.CommaToken && isMatchingReference(source, target.right)); } switch (source.kind) { + case SyntaxKind.MetaProperty: + return target.kind === SyntaxKind.MetaProperty + && (source as MetaProperty).keywordToken === SyntaxKind.ImportKeyword + && (target as MetaProperty).keywordToken === SyntaxKind.ImportKeyword + && (source as MetaProperty).name.escapedText === "meta" + && (target as MetaProperty).name.escapedText === "meta"; case SyntaxKind.Identifier: case SyntaxKind.PrivateIdentifier: return target.kind === SyntaxKind.Identifier && getResolvedSymbol(source) === getResolvedSymbol(target) || diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 6f7a3d9bd55..3fb9d039bf0 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4836,9 +4836,12 @@ namespace ts { } export function isDottedName(node: Expression): boolean { - return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword || - node.kind === SyntaxKind.PropertyAccessExpression && isDottedName((node).expression) || - node.kind === SyntaxKind.ParenthesizedExpression && isDottedName((node).expression); + return node.kind === SyntaxKind.Identifier + || node.kind === SyntaxKind.ThisKeyword + || node.kind === SyntaxKind.SuperKeyword + || node.kind === SyntaxKind.MetaProperty + || node.kind === SyntaxKind.PropertyAccessExpression && isDottedName((node).expression) + || node.kind === SyntaxKind.ParenthesizedExpression && isDottedName((node).expression); } export function isPropertyAccessEntityNameExpression(node: Node): node is PropertyAccessEntityNameExpression { diff --git a/tests/baselines/reference/importMetaNarrowing(module=es2020).js b/tests/baselines/reference/importMetaNarrowing(module=es2020).js new file mode 100644 index 00000000000..f35dbf8cd1b --- /dev/null +++ b/tests/baselines/reference/importMetaNarrowing(module=es2020).js @@ -0,0 +1,14 @@ +//// [importMetaNarrowing.ts] +declare global { interface ImportMeta {foo?: () => void} }; + +if (import.meta.foo) { + import.meta.foo(); +} + + +//// [importMetaNarrowing.js] +; +if (import.meta.foo) { + import.meta.foo(); +} +export {}; diff --git a/tests/baselines/reference/importMetaNarrowing(module=es2020).symbols b/tests/baselines/reference/importMetaNarrowing(module=es2020).symbols new file mode 100644 index 00000000000..6ae71cd6446 --- /dev/null +++ b/tests/baselines/reference/importMetaNarrowing(module=es2020).symbols @@ -0,0 +1,15 @@ +=== tests/cases/conformance/es2019/importMeta/importMetaNarrowing.ts === +declare global { interface ImportMeta {foo?: () => void} }; +>global : Symbol(global, Decl(importMetaNarrowing.ts, 0, 0)) +>ImportMeta : Symbol(ImportMeta, Decl(lib.es5.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(importMetaNarrowing.ts, 0, 16)) +>foo : Symbol(ImportMeta.foo, Decl(importMetaNarrowing.ts, 0, 39)) + +if (import.meta.foo) { +>import.meta.foo : Symbol(ImportMeta.foo, Decl(importMetaNarrowing.ts, 0, 39)) +>foo : Symbol(ImportMeta.foo, Decl(importMetaNarrowing.ts, 0, 39)) + + import.meta.foo(); +>import.meta.foo : Symbol(ImportMeta.foo, Decl(importMetaNarrowing.ts, 0, 39)) +>foo : Symbol(ImportMeta.foo, Decl(importMetaNarrowing.ts, 0, 39)) +} + diff --git a/tests/baselines/reference/importMetaNarrowing(module=es2020).types b/tests/baselines/reference/importMetaNarrowing(module=es2020).types new file mode 100644 index 00000000000..46ade26a5de --- /dev/null +++ b/tests/baselines/reference/importMetaNarrowing(module=es2020).types @@ -0,0 +1,19 @@ +=== tests/cases/conformance/es2019/importMeta/importMetaNarrowing.ts === +declare global { interface ImportMeta {foo?: () => void} }; +>global : any +>foo : (() => void) | undefined + +if (import.meta.foo) { +>import.meta.foo : (() => void) | undefined +>import.meta : ImportMeta +>meta : any +>foo : (() => void) | undefined + + import.meta.foo(); +>import.meta.foo() : void +>import.meta.foo : () => void +>import.meta : ImportMeta +>meta : any +>foo : () => void +} + diff --git a/tests/baselines/reference/importMetaNarrowing(module=esnext).js b/tests/baselines/reference/importMetaNarrowing(module=esnext).js new file mode 100644 index 00000000000..f35dbf8cd1b --- /dev/null +++ b/tests/baselines/reference/importMetaNarrowing(module=esnext).js @@ -0,0 +1,14 @@ +//// [importMetaNarrowing.ts] +declare global { interface ImportMeta {foo?: () => void} }; + +if (import.meta.foo) { + import.meta.foo(); +} + + +//// [importMetaNarrowing.js] +; +if (import.meta.foo) { + import.meta.foo(); +} +export {}; diff --git a/tests/baselines/reference/importMetaNarrowing(module=esnext).symbols b/tests/baselines/reference/importMetaNarrowing(module=esnext).symbols new file mode 100644 index 00000000000..6ae71cd6446 --- /dev/null +++ b/tests/baselines/reference/importMetaNarrowing(module=esnext).symbols @@ -0,0 +1,15 @@ +=== tests/cases/conformance/es2019/importMeta/importMetaNarrowing.ts === +declare global { interface ImportMeta {foo?: () => void} }; +>global : Symbol(global, Decl(importMetaNarrowing.ts, 0, 0)) +>ImportMeta : Symbol(ImportMeta, Decl(lib.es5.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(importMetaNarrowing.ts, 0, 16)) +>foo : Symbol(ImportMeta.foo, Decl(importMetaNarrowing.ts, 0, 39)) + +if (import.meta.foo) { +>import.meta.foo : Symbol(ImportMeta.foo, Decl(importMetaNarrowing.ts, 0, 39)) +>foo : Symbol(ImportMeta.foo, Decl(importMetaNarrowing.ts, 0, 39)) + + import.meta.foo(); +>import.meta.foo : Symbol(ImportMeta.foo, Decl(importMetaNarrowing.ts, 0, 39)) +>foo : Symbol(ImportMeta.foo, Decl(importMetaNarrowing.ts, 0, 39)) +} + diff --git a/tests/baselines/reference/importMetaNarrowing(module=esnext).types b/tests/baselines/reference/importMetaNarrowing(module=esnext).types new file mode 100644 index 00000000000..46ade26a5de --- /dev/null +++ b/tests/baselines/reference/importMetaNarrowing(module=esnext).types @@ -0,0 +1,19 @@ +=== tests/cases/conformance/es2019/importMeta/importMetaNarrowing.ts === +declare global { interface ImportMeta {foo?: () => void} }; +>global : any +>foo : (() => void) | undefined + +if (import.meta.foo) { +>import.meta.foo : (() => void) | undefined +>import.meta : ImportMeta +>meta : any +>foo : (() => void) | undefined + + import.meta.foo(); +>import.meta.foo() : void +>import.meta.foo : () => void +>import.meta : ImportMeta +>meta : any +>foo : () => void +} + diff --git a/tests/baselines/reference/importMetaNarrowing(module=system).js b/tests/baselines/reference/importMetaNarrowing(module=system).js new file mode 100644 index 00000000000..eff8a276c19 --- /dev/null +++ b/tests/baselines/reference/importMetaNarrowing(module=system).js @@ -0,0 +1,22 @@ +//// [importMetaNarrowing.ts] +declare global { interface ImportMeta {foo?: () => void} }; + +if (import.meta.foo) { + import.meta.foo(); +} + + +//// [importMetaNarrowing.js] +System.register([], function (exports_1, context_1) { + "use strict"; + var __moduleName = context_1 && context_1.id; + return { + setters: [], + execute: function () { + ; + if (context_1.meta.foo) { + context_1.meta.foo(); + } + } + }; +}); diff --git a/tests/baselines/reference/importMetaNarrowing(module=system).symbols b/tests/baselines/reference/importMetaNarrowing(module=system).symbols new file mode 100644 index 00000000000..6ae71cd6446 --- /dev/null +++ b/tests/baselines/reference/importMetaNarrowing(module=system).symbols @@ -0,0 +1,15 @@ +=== tests/cases/conformance/es2019/importMeta/importMetaNarrowing.ts === +declare global { interface ImportMeta {foo?: () => void} }; +>global : Symbol(global, Decl(importMetaNarrowing.ts, 0, 0)) +>ImportMeta : Symbol(ImportMeta, Decl(lib.es5.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(importMetaNarrowing.ts, 0, 16)) +>foo : Symbol(ImportMeta.foo, Decl(importMetaNarrowing.ts, 0, 39)) + +if (import.meta.foo) { +>import.meta.foo : Symbol(ImportMeta.foo, Decl(importMetaNarrowing.ts, 0, 39)) +>foo : Symbol(ImportMeta.foo, Decl(importMetaNarrowing.ts, 0, 39)) + + import.meta.foo(); +>import.meta.foo : Symbol(ImportMeta.foo, Decl(importMetaNarrowing.ts, 0, 39)) +>foo : Symbol(ImportMeta.foo, Decl(importMetaNarrowing.ts, 0, 39)) +} + diff --git a/tests/baselines/reference/importMetaNarrowing(module=system).types b/tests/baselines/reference/importMetaNarrowing(module=system).types new file mode 100644 index 00000000000..46ade26a5de --- /dev/null +++ b/tests/baselines/reference/importMetaNarrowing(module=system).types @@ -0,0 +1,19 @@ +=== tests/cases/conformance/es2019/importMeta/importMetaNarrowing.ts === +declare global { interface ImportMeta {foo?: () => void} }; +>global : any +>foo : (() => void) | undefined + +if (import.meta.foo) { +>import.meta.foo : (() => void) | undefined +>import.meta : ImportMeta +>meta : any +>foo : (() => void) | undefined + + import.meta.foo(); +>import.meta.foo() : void +>import.meta.foo : () => void +>import.meta : ImportMeta +>meta : any +>foo : () => void +} + diff --git a/tests/cases/conformance/es2019/importMeta/importMetaNarrowing.ts b/tests/cases/conformance/es2019/importMeta/importMetaNarrowing.ts new file mode 100644 index 00000000000..4a855cff0c4 --- /dev/null +++ b/tests/cases/conformance/es2019/importMeta/importMetaNarrowing.ts @@ -0,0 +1,8 @@ +// @module: esnext,system,es2020 +// @strict: true + +declare global { interface ImportMeta {foo?: () => void} }; + +if (import.meta.foo) { + import.meta.foo(); +}