diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 08c67097a9c..5bf1f022869 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1730,14 +1730,19 @@ namespace ts { : resolveSymbol(moduleSymbol.exports.get(name), dontResolveAlias); } + function isSyntacticDefault(node: Node) { + return ((isExportAssignment(node) && !node.isExportEquals) || hasModifier(node, ModifierFlags.Default)); + } + 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 +1782,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 00000000000..22db68eb1fb --- /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 00000000000..e95a773c716 --- /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 00000000000..4901f0fa5b8 --- /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 00000000000..2dd739230b8 --- /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);