From 607a98d85c9c25e2f877635fe0fd8add65029adb Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 22 Mar 2018 15:57:12 -0700 Subject: [PATCH 1/2] Tighten heuristic for definite dts moduleness to check for syntactic default exports --- src/compiler/checker.ts | 17 +++++++-- ...tMemberMustBeSyntacticallyDefaultExport.js | 29 +++++++++++++++ ...erMustBeSyntacticallyDefaultExport.symbols | 33 +++++++++++++++++ ...mberMustBeSyntacticallyDefaultExport.types | 36 +++++++++++++++++++ ...tMemberMustBeSyntacticallyDefaultExport.ts | 17 +++++++++ 5 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.js create mode 100644 tests/baselines/reference/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.symbols create mode 100644 tests/baselines/reference/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.types create mode 100644 tests/cases/compiler/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 267da5727370b..9af6b96510270 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1730,14 +1730,25 @@ namespace ts { : resolveSymbol(moduleSymbol.exports.get(name), dontResolveAlias); } + function isSyntacticDefault(node: Node) { + if (isExportAssignment(node) && !node.isExportEquals) { + return true; + } + if (hasModifier(node, ModifierFlags.Default)) { + return true; + } + return false; + } + function canHaveSyntheticDefault(file: SourceFile | undefined, moduleSymbol: Symbol, dontResolveAlias: boolean) { if (!allowSyntheticDefaultImports) { return false; } // Declaration files (and ambient modules) if (!file || file.isDeclarationFile) { - // Definitely cannot have a synthetic default if they have a default member specified - if (resolveExportByName(moduleSymbol, InternalSymbolName.Default, dontResolveAlias)) { + // Definitely cannot have a synthetic default if they have a syntactic default member specified + const defaultExportSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, dontResolveAlias); + if (defaultExportSymbol && defaultExportSymbol.valueDeclaration && isSyntacticDefault(defaultExportSymbol.valueDeclaration)) { return false; } // It _might_ still be incorrect to assume there is no __esModule marker on the import at runtime, even if there is no `default` member @@ -1777,7 +1788,7 @@ namespace ts { if (!exportDefaultSymbol && !hasSyntheticDefault) { error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol)); } - else if (!exportDefaultSymbol && hasSyntheticDefault) { + else if (hasSyntheticDefault) { // per emit behavior, a synthetic default overrides a "real" .default member if `__esModule` is not present return resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias); } diff --git a/tests/baselines/reference/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.js b/tests/baselines/reference/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.js new file mode 100644 index 0000000000000..22db68eb1fba2 --- /dev/null +++ b/tests/baselines/reference/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.js @@ -0,0 +1,29 @@ +//// [tests/cases/compiler/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.ts] //// + +//// [point.d.ts] +declare class Point { + x: number; + y: number; + + constructor(x: number, y: number); + + static default: "foo"; +} + +export = Point; +//// [index.ts] +import Point from "./point"; + +const C = Point; +const p = new C(1, 2); + + +//// [index.js] +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +exports.__esModule = true; +var point_1 = __importDefault(require("./point")); +var C = point_1["default"]; +var p = new C(1, 2); diff --git a/tests/baselines/reference/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.symbols b/tests/baselines/reference/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.symbols new file mode 100644 index 0000000000000..e95a773c71619 --- /dev/null +++ b/tests/baselines/reference/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.symbols @@ -0,0 +1,33 @@ +=== tests/cases/compiler/point.d.ts === +declare class Point { +>Point : Symbol(Point, Decl(point.d.ts, 0, 0)) + + x: number; +>x : Symbol(Point.x, Decl(point.d.ts, 0, 21)) + + y: number; +>y : Symbol(Point.y, Decl(point.d.ts, 1, 14)) + + constructor(x: number, y: number); +>x : Symbol(x, Decl(point.d.ts, 4, 16)) +>y : Symbol(y, Decl(point.d.ts, 4, 26)) + + static default: "foo"; +>default : Symbol(Point.default, Decl(point.d.ts, 4, 38)) +} + +export = Point; +>Point : Symbol(Point, Decl(point.d.ts, 0, 0)) + +=== tests/cases/compiler/index.ts === +import Point from "./point"; +>Point : Symbol(Point, Decl(index.ts, 0, 6)) + +const C = Point; +>C : Symbol(C, Decl(index.ts, 2, 5)) +>Point : Symbol(Point, Decl(index.ts, 0, 6)) + +const p = new C(1, 2); +>p : Symbol(p, Decl(index.ts, 3, 5)) +>C : Symbol(C, Decl(index.ts, 2, 5)) + diff --git a/tests/baselines/reference/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.types b/tests/baselines/reference/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.types new file mode 100644 index 0000000000000..4901f0fa5b8be --- /dev/null +++ b/tests/baselines/reference/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.types @@ -0,0 +1,36 @@ +=== tests/cases/compiler/point.d.ts === +declare class Point { +>Point : Point + + x: number; +>x : number + + y: number; +>y : number + + constructor(x: number, y: number); +>x : number +>y : number + + static default: "foo"; +>default : "foo" +} + +export = Point; +>Point : Point + +=== tests/cases/compiler/index.ts === +import Point from "./point"; +>Point : typeof Point + +const C = Point; +>C : typeof Point +>Point : typeof Point + +const p = new C(1, 2); +>p : Point +>new C(1, 2) : Point +>C : typeof Point +>1 : 1 +>2 : 2 + diff --git a/tests/cases/compiler/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.ts b/tests/cases/compiler/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.ts new file mode 100644 index 0000000000000..2dd739230b8bd --- /dev/null +++ b/tests/cases/compiler/esModuleInteropDefaultMemberMustBeSyntacticallyDefaultExport.ts @@ -0,0 +1,17 @@ +// @esModuleInterop: true +// @filename: point.d.ts +declare class Point { + x: number; + y: number; + + constructor(x: number, y: number); + + static default: "foo"; +} + +export = Point; +// @filename: index.ts +import Point from "./point"; + +const C = Point; +const p = new C(1, 2); From 70ab527b7820a1b7b123ec074b8301bf715f8f71 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 26 Mar 2018 15:57:08 -0700 Subject: [PATCH 2/2] Inline function --- src/compiler/checker.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9af6b96510270..3ea83e419732e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1731,13 +1731,7 @@ namespace ts { } function isSyntacticDefault(node: Node) { - if (isExportAssignment(node) && !node.isExportEquals) { - return true; - } - if (hasModifier(node, ModifierFlags.Default)) { - return true; - } - return false; + return ((isExportAssignment(node) && !node.isExportEquals) || hasModifier(node, ModifierFlags.Default)); } function canHaveSyntheticDefault(file: SourceFile | undefined, moduleSymbol: Symbol, dontResolveAlias: boolean) {