From 854c124a10773ef069bc065a0bbd24438c434171 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Thu, 5 Apr 2018 16:33:07 -0700
Subject: [PATCH 1/5] Combining symbol and removing error done but messy
---
src/compiler/checker.ts | 45 ++++++++++++++++---
...oduleExportWithExportPropertyAssignment.ts | 12 +++++
2 files changed, 51 insertions(+), 6 deletions(-)
create mode 100644 tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment.ts
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 34e5000790add..1a4a6a8248cd1 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -2096,9 +2096,15 @@ namespace ts {
const moduleName = (namespace.valueDeclaration.initializer as CallExpression).arguments[0] as StringLiteral;
const moduleSym = resolveExternalModuleName(moduleName, moduleName);
if (moduleSym) {
- const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym);
- if (resolvedModuleSymbol) {
- namespace = resolvedModuleSymbol;
+ if (moduleSym.exports.has(InternalSymbolName.ExportEquals as __String) && moduleSym.exports.size > 1) {
+ Debug.assert(!!(moduleSym.valueDeclaration as SourceFile).commonJsModuleIndicator);
+ namespace = combineCommonJsSymbol(moduleSym);
+ }
+ else {
+ const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym);
+ if (resolvedModuleSymbol) {
+ namespace = resolvedModuleSymbol;
+ }
}
}
}
@@ -4350,7 +4356,8 @@ namespace ts {
return unknownType;
}
- if (isPropertyAccessExpression(expression.left) && expression.left.expression.kind === SyntaxKind.ThisKeyword) {
+ const special = getSpecialPropertyAssignmentKind(expression);
+ if (special === SpecialPropertyAssignmentKind.ThisProperty) {
const thisContainer = getThisContainer(expression, /*includeArrowFunctions*/ false);
// Properties defined in a constructor (or javascript constructor function) don't get undefined added.
// Function expressions that are assigned to the prototype count as methods.
@@ -4380,7 +4387,17 @@ namespace ts {
}
else if (!jsDocType) {
// If we don't have an explicit JSDoc type, get the type from the expression.
- const type = getWidenedLiteralType(checkExpressionCached(expression.right));
+ let type = getWidenedLiteralType(checkExpressionCached(expression.right));
+
+ if (getObjectFlags(type) & ObjectFlags.Anonymous &&
+ special === SpecialPropertyAssignmentKind.ModuleExports &&
+ symbol.escapedName === InternalSymbolName.ExportEquals) {
+ const o = resolveStructuredTypeMembers(type as AnonymousType);
+ const members = createSymbolTable();
+ copyEntries(o.members, members);
+ copyEntries(symbol.exports, members);
+ type = createAnonymousType(symbol, members, o.callSignatures, o.constructSignatures, o.stringIndexInfo, o.numberIndexInfo);
+ }
let anyedType = type;
if (isEmptyArrayLiteralType(type)) {
anyedType = anyArrayType;
@@ -6992,6 +7009,10 @@ namespace ts {
function resolveExternalModuleTypeByLiteral(name: StringLiteral) {
const moduleSym = resolveExternalModuleName(name, name);
if (moduleSym) {
+ if (moduleSym.exports.has(InternalSymbolName.ExportEquals as __String) && moduleSym.exports.size > 1) {
+ Debug.assert(!!(moduleSym.valueDeclaration as SourceFile).commonJsModuleIndicator);
+ return getTypeOfSymbol(combineCommonJsSymbol(moduleSym));
+ }
const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym);
if (resolvedModuleSymbol) {
return getTypeOfSymbol(resolvedModuleSymbol);
@@ -7001,6 +7022,18 @@ namespace ts {
return anyType;
}
+ function combineCommonJsSymbol(moduleSym: Symbol): Symbol {
+ const combined = cloneSymbol(moduleSym.exports.get(InternalSymbolName.ExportEquals));
+ moduleSym.exports.forEach((s,name) => {
+ if (name === InternalSymbolName.ExportEquals) return;
+ if (!combined.exports.has(name) || combined.exports.get(name).valueDeclaration.pos > s.valueDeclaration.pos) {
+ combined.exports.set(name, s);
+ }
+ });
+ return combined;
+
+ }
+
function getThisTypeOfSignature(signature: Signature): Type | undefined {
if (signature.thisParameter) {
return getTypeOfSymbol(signature.thisParameter);
@@ -24568,7 +24601,7 @@ namespace ts {
const exportEqualsSymbol = moduleSymbol.exports.get("export=" as __String);
if (exportEqualsSymbol && hasExportedMembers(moduleSymbol)) {
const declaration = getDeclarationOfAliasSymbol(exportEqualsSymbol) || exportEqualsSymbol.valueDeclaration;
- if (!isTopLevelInExternalModuleAugmentation(declaration)) {
+ if (!isTopLevelInExternalModuleAugmentation(declaration) && !isInJavaScriptFile(declaration)) {
error(declaration, Diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements);
}
}
diff --git a/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment.ts b/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment.ts
new file mode 100644
index 0000000000000..9486f238e74e4
--- /dev/null
+++ b/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment.ts
@@ -0,0 +1,12 @@
+// @noEmit: true
+// @allowJs: true
+// @checkJs: true
+// @Filename: mod1.js
+// TODO: Test that this is not legal (or at least doesn't work) if module.exports is a primitive
+module.exports = function () { }
+module.exports.f = function () { }
+
+// @Filename: a.js
+var mod1 = require('./mod1')
+mod1()
+mod1.f()
From 8b67c9b799bb322f4e9cf49654404e0c0a7ee52d Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Thu, 5 Apr 2018 16:38:31 -0700
Subject: [PATCH 2/5] Small fix + add new test baselines
All other tests are unchanged
---
src/compiler/checker.ts | 2 +-
...ortWithExportPropertyAssignment.errors.txt | 21 +++++++++++
...ExportWithExportPropertyAssignment.symbols | 23 ++++++++++++
...leExportWithExportPropertyAssignment.types | 35 +++++++++++++++++++
4 files changed, 80 insertions(+), 1 deletion(-)
create mode 100644 tests/baselines/reference/moduleExportWithExportPropertyAssignment.errors.txt
create mode 100644 tests/baselines/reference/moduleExportWithExportPropertyAssignment.symbols
create mode 100644 tests/baselines/reference/moduleExportWithExportPropertyAssignment.types
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 1a4a6a8248cd1..5cf682b5b6c95 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -4396,7 +4396,7 @@ namespace ts {
const members = createSymbolTable();
copyEntries(o.members, members);
copyEntries(symbol.exports, members);
- type = createAnonymousType(symbol, members, o.callSignatures, o.constructSignatures, o.stringIndexInfo, o.numberIndexInfo);
+ type = createAnonymousType(o.symbol, members, o.callSignatures, o.constructSignatures, o.stringIndexInfo, o.numberIndexInfo);
}
let anyedType = type;
if (isEmptyArrayLiteralType(type)) {
diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment.errors.txt b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.errors.txt
new file mode 100644
index 0000000000000..63c13c96c136b
--- /dev/null
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.errors.txt
@@ -0,0 +1,21 @@
+tests/cases/conformance/salsa/a.js(1,12): error TS2304: Cannot find name 'require'.
+tests/cases/conformance/salsa/mod1.js(2,1): error TS2304: Cannot find name 'module'.
+tests/cases/conformance/salsa/mod1.js(3,1): error TS2304: Cannot find name 'module'.
+
+
+==== tests/cases/conformance/salsa/a.js (1 errors) ====
+ var mod1 = require('./mod1')
+ ~~~~~~~
+!!! error TS2304: Cannot find name 'require'.
+ mod1()
+ mod1.f()
+
+==== tests/cases/conformance/salsa/mod1.js (2 errors) ====
+ // TODO: Test that this is not legal (or at least doesn't work) if module.exports is a primitive
+ module.exports = function () { }
+ ~~~~~~
+!!! error TS2304: Cannot find name 'module'.
+ module.exports.f = function () { }
+ ~~~~~~
+!!! error TS2304: Cannot find name 'module'.
+
\ No newline at end of file
diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment.symbols b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.symbols
new file mode 100644
index 0000000000000..77fb9fd209745
--- /dev/null
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.symbols
@@ -0,0 +1,23 @@
+=== tests/cases/conformance/salsa/a.js ===
+var mod1 = require('./mod1')
+>mod1 : Symbol(mod1, Decl(a.js, 0, 3))
+>'./mod1' : Symbol("tests/cases/conformance/salsa/mod1", Decl(mod1.js, 0, 0))
+
+mod1()
+>mod1 : Symbol(mod1, Decl(a.js, 0, 3))
+
+mod1.f()
+>mod1.f : Symbol(f, Decl(mod1.js, 1, 32))
+>mod1 : Symbol(mod1, Decl(a.js, 0, 3))
+>f : Symbol(f, Decl(mod1.js, 1, 32))
+
+=== tests/cases/conformance/salsa/mod1.js ===
+// TODO: Test that this is not legal (or at least doesn't work) if module.exports is a primitive
+module.exports = function () { }
+>module : Symbol(export=, Decl(mod1.js, 0, 0))
+>exports : Symbol(export=, Decl(mod1.js, 0, 0))
+
+module.exports.f = function () { }
+>module.exports : Symbol(f, Decl(mod1.js, 1, 32))
+>f : Symbol(f, Decl(mod1.js, 1, 32))
+
diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment.types b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.types
new file mode 100644
index 0000000000000..d1657107fdfda
--- /dev/null
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.types
@@ -0,0 +1,35 @@
+=== tests/cases/conformance/salsa/a.js ===
+var mod1 = require('./mod1')
+>mod1 : { (): void; f: () => void; }
+>require('./mod1') : { (): void; f: () => void; }
+>require : any
+>'./mod1' : "./mod1"
+
+mod1()
+>mod1() : void
+>mod1 : { (): void; f: () => void; }
+
+mod1.f()
+>mod1.f() : void
+>mod1.f : () => void
+>mod1 : { (): void; f: () => void; }
+>f : () => void
+
+=== tests/cases/conformance/salsa/mod1.js ===
+// TODO: Test that this is not legal (or at least doesn't work) if module.exports is a primitive
+module.exports = function () { }
+>module.exports = function () { } : () => void
+>module.exports : any
+>module : any
+>exports : any
+>function () { } : () => void
+
+module.exports.f = function () { }
+>module.exports.f = function () { } : () => void
+>module.exports.f : any
+>module.exports : any
+>module : any
+>exports : any
+>f : any
+>function () { } : () => void
+
From 571e9350cbbbb5364f23172f81cf48c73a74a064 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Fri, 6 Apr 2018 10:44:21 -0700
Subject: [PATCH 3/5] Union conflicting assignment types+better names
---
src/compiler/checker.ts | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 5cf682b5b6c95..a33421c946dd5 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -2098,7 +2098,7 @@ namespace ts {
if (moduleSym) {
if (moduleSym.exports.has(InternalSymbolName.ExportEquals as __String) && moduleSym.exports.size > 1) {
Debug.assert(!!(moduleSym.valueDeclaration as SourceFile).commonJsModuleIndicator);
- namespace = combineCommonJsSymbol(moduleSym);
+ namespace = mergeCommonJsExportEquals(moduleSym);
}
else {
const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym);
@@ -4392,11 +4392,27 @@ namespace ts {
if (getObjectFlags(type) & ObjectFlags.Anonymous &&
special === SpecialPropertyAssignmentKind.ModuleExports &&
symbol.escapedName === InternalSymbolName.ExportEquals) {
- const o = resolveStructuredTypeMembers(type as AnonymousType);
+ const exportedType = resolveStructuredTypeMembers(type as AnonymousType);
const members = createSymbolTable();
- copyEntries(o.members, members);
- copyEntries(symbol.exports, members);
- type = createAnonymousType(o.symbol, members, o.callSignatures, o.constructSignatures, o.stringIndexInfo, o.numberIndexInfo);
+ copyEntries(exportedType.members, members);
+ symbol.exports.forEach((s, name) => {
+ if (members.has(name)) {
+ const exportedMember = exportedType.members.get(name);
+ const union = createSymbol(s.flags | exportedMember.flags, name);
+ union.type = getUnionType([getTypeOfSymbol(s), getTypeOfSymbol(exportedMember)]);
+ members.set(name, union);
+ }
+ else {
+ members.set(name, s);
+ }
+ });
+ type = createAnonymousType(
+ exportedType.symbol,
+ members,
+ exportedType.callSignatures,
+ exportedType.constructSignatures,
+ exportedType.stringIndexInfo,
+ exportedType.numberIndexInfo);
}
let anyedType = type;
if (isEmptyArrayLiteralType(type)) {
@@ -7011,7 +7027,7 @@ namespace ts {
if (moduleSym) {
if (moduleSym.exports.has(InternalSymbolName.ExportEquals as __String) && moduleSym.exports.size > 1) {
Debug.assert(!!(moduleSym.valueDeclaration as SourceFile).commonJsModuleIndicator);
- return getTypeOfSymbol(combineCommonJsSymbol(moduleSym));
+ return getTypeOfSymbol(mergeCommonJsExportEquals(moduleSym));
}
const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym);
if (resolvedModuleSymbol) {
@@ -7022,11 +7038,11 @@ namespace ts {
return anyType;
}
- function combineCommonJsSymbol(moduleSym: Symbol): Symbol {
+ function mergeCommonJsExportEquals(moduleSym: Symbol): Symbol {
const combined = cloneSymbol(moduleSym.exports.get(InternalSymbolName.ExportEquals));
- moduleSym.exports.forEach((s,name) => {
+ moduleSym.exports.forEach((s, name) => {
if (name === InternalSymbolName.ExportEquals) return;
- if (!combined.exports.has(name) || combined.exports.get(name).valueDeclaration.pos > s.valueDeclaration.pos) {
+ if (!combined.exports.has(name)) {
combined.exports.set(name, s);
}
});
From 6203ec7ec1223a60f50f28adec6fc3ca8b200e94 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Fri, 6 Apr 2018 10:44:48 -0700
Subject: [PATCH 4/5] Add tests and update baselines
---
...ortWithExportPropertyAssignment.errors.txt | 25 ++--
...ExportWithExportPropertyAssignment.symbols | 28 ++++-
...leExportWithExportPropertyAssignment.types | 40 +++---
...rtWithExportPropertyAssignment2.errors.txt | 19 +++
...xportWithExportPropertyAssignment2.symbols | 37 ++++++
...eExportWithExportPropertyAssignment2.types | 48 ++++++++
...rtWithExportPropertyAssignment3.errors.txt | 34 ++++++
...xportWithExportPropertyAssignment3.symbols | 74 +++++++++++
...eExportWithExportPropertyAssignment3.types | 96 +++++++++++++++
...rtWithExportPropertyAssignment4.errors.txt | 36 ++++++
...xportWithExportPropertyAssignment4.symbols | 87 +++++++++++++
...eExportWithExportPropertyAssignment4.types | 115 ++++++++++++++++++
...oduleExportWithExportPropertyAssignment.ts | 11 +-
...duleExportWithExportPropertyAssignment2.ts | 16 +++
...duleExportWithExportPropertyAssignment3.ts | 24 ++++
...duleExportWithExportPropertyAssignment4.ts | 26 ++++
16 files changed, 680 insertions(+), 36 deletions(-)
create mode 100644 tests/baselines/reference/moduleExportWithExportPropertyAssignment2.errors.txt
create mode 100644 tests/baselines/reference/moduleExportWithExportPropertyAssignment2.symbols
create mode 100644 tests/baselines/reference/moduleExportWithExportPropertyAssignment2.types
create mode 100644 tests/baselines/reference/moduleExportWithExportPropertyAssignment3.errors.txt
create mode 100644 tests/baselines/reference/moduleExportWithExportPropertyAssignment3.symbols
create mode 100644 tests/baselines/reference/moduleExportWithExportPropertyAssignment3.types
create mode 100644 tests/baselines/reference/moduleExportWithExportPropertyAssignment4.errors.txt
create mode 100644 tests/baselines/reference/moduleExportWithExportPropertyAssignment4.symbols
create mode 100644 tests/baselines/reference/moduleExportWithExportPropertyAssignment4.types
create mode 100644 tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment2.ts
create mode 100644 tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment3.ts
create mode 100644 tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment4.ts
diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment.errors.txt b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.errors.txt
index 63c13c96c136b..aab334e77f38c 100644
--- a/tests/baselines/reference/moduleExportWithExportPropertyAssignment.errors.txt
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.errors.txt
@@ -1,21 +1,20 @@
-tests/cases/conformance/salsa/a.js(1,12): error TS2304: Cannot find name 'require'.
-tests/cases/conformance/salsa/mod1.js(2,1): error TS2304: Cannot find name 'module'.
-tests/cases/conformance/salsa/mod1.js(3,1): error TS2304: Cannot find name 'module'.
+tests/cases/conformance/salsa/a.js(4,1): error TS2554: Expected 1 arguments, but got 0.
==== tests/cases/conformance/salsa/a.js (1 errors) ====
+ ///
var mod1 = require('./mod1')
- ~~~~~~~
-!!! error TS2304: Cannot find name 'require'.
mod1()
- mod1.f()
+ mod1.f() // error, not enough arguments
+ ~~~~~~~~
+!!! error TS2554: Expected 1 arguments, but got 0.
-==== tests/cases/conformance/salsa/mod1.js (2 errors) ====
- // TODO: Test that this is not legal (or at least doesn't work) if module.exports is a primitive
+==== tests/cases/conformance/salsa/requires.d.ts (0 errors) ====
+ declare var module: { exports: any };
+ declare function require(name: string): any;
+==== tests/cases/conformance/salsa/mod1.js (0 errors) ====
+ ///
module.exports = function () { }
- ~~~~~~
-!!! error TS2304: Cannot find name 'module'.
- module.exports.f = function () { }
- ~~~~~~
-!!! error TS2304: Cannot find name 'module'.
+ /** @param {number} a */
+ module.exports.f = function (a) { }
\ No newline at end of file
diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment.symbols b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.symbols
index 77fb9fd209745..c5120a80571e3 100644
--- a/tests/baselines/reference/moduleExportWithExportPropertyAssignment.symbols
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.symbols
@@ -1,23 +1,39 @@
=== tests/cases/conformance/salsa/a.js ===
+///
var mod1 = require('./mod1')
->mod1 : Symbol(mod1, Decl(a.js, 0, 3))
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
+>require : Symbol(require, Decl(requires.d.ts, 0, 37))
>'./mod1' : Symbol("tests/cases/conformance/salsa/mod1", Decl(mod1.js, 0, 0))
mod1()
->mod1 : Symbol(mod1, Decl(a.js, 0, 3))
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
-mod1.f()
+mod1.f() // error, not enough arguments
>mod1.f : Symbol(f, Decl(mod1.js, 1, 32))
->mod1 : Symbol(mod1, Decl(a.js, 0, 3))
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
>f : Symbol(f, Decl(mod1.js, 1, 32))
+=== tests/cases/conformance/salsa/requires.d.ts ===
+declare var module: { exports: any };
+>module : Symbol(module, Decl(requires.d.ts, 0, 11))
+>exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
+
+declare function require(name: string): any;
+>require : Symbol(require, Decl(requires.d.ts, 0, 37))
+>name : Symbol(name, Decl(requires.d.ts, 1, 25))
+
=== tests/cases/conformance/salsa/mod1.js ===
-// TODO: Test that this is not legal (or at least doesn't work) if module.exports is a primitive
+///
module.exports = function () { }
+>module.exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
>module : Symbol(export=, Decl(mod1.js, 0, 0))
>exports : Symbol(export=, Decl(mod1.js, 0, 0))
-module.exports.f = function () { }
+/** @param {number} a */
+module.exports.f = function (a) { }
>module.exports : Symbol(f, Decl(mod1.js, 1, 32))
+>module : Symbol(module, Decl(requires.d.ts, 0, 11))
+>exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
>f : Symbol(f, Decl(mod1.js, 1, 32))
+>a : Symbol(a, Decl(mod1.js, 3, 29))
diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment.types b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.types
index d1657107fdfda..b7170842c1f39 100644
--- a/tests/baselines/reference/moduleExportWithExportPropertyAssignment.types
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.types
@@ -1,35 +1,47 @@
=== tests/cases/conformance/salsa/a.js ===
+///
var mod1 = require('./mod1')
->mod1 : { (): void; f: () => void; }
->require('./mod1') : { (): void; f: () => void; }
->require : any
+>mod1 : { (): void; f: (a: number) => void; }
+>require('./mod1') : { (): void; f: (a: number) => void; }
+>require : (name: string) => any
>'./mod1' : "./mod1"
mod1()
>mod1() : void
->mod1 : { (): void; f: () => void; }
+>mod1 : { (): void; f: (a: number) => void; }
-mod1.f()
+mod1.f() // error, not enough arguments
>mod1.f() : void
->mod1.f : () => void
->mod1 : { (): void; f: () => void; }
->f : () => void
+>mod1.f : (a: number) => void
+>mod1 : { (): void; f: (a: number) => void; }
+>f : (a: number) => void
+
+=== tests/cases/conformance/salsa/requires.d.ts ===
+declare var module: { exports: any };
+>module : { exports: any; }
+>exports : any
+
+declare function require(name: string): any;
+>require : (name: string) => any
+>name : string
=== tests/cases/conformance/salsa/mod1.js ===
-// TODO: Test that this is not legal (or at least doesn't work) if module.exports is a primitive
+///
module.exports = function () { }
>module.exports = function () { } : () => void
>module.exports : any
->module : any
+>module : { exports: any; }
>exports : any
>function () { } : () => void
-module.exports.f = function () { }
->module.exports.f = function () { } : () => void
+/** @param {number} a */
+module.exports.f = function (a) { }
+>module.exports.f = function (a) { } : (a: number) => void
>module.exports.f : any
>module.exports : any
->module : any
+>module : { exports: any; }
>exports : any
>f : any
->function () { } : () => void
+>function (a) { } : (a: number) => void
+>a : number
diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment2.errors.txt b/tests/baselines/reference/moduleExportWithExportPropertyAssignment2.errors.txt
new file mode 100644
index 0000000000000..66cb517a25be7
--- /dev/null
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment2.errors.txt
@@ -0,0 +1,19 @@
+tests/cases/conformance/salsa/a.js(4,6): error TS2339: Property 'f' does not exist on type 'number'.
+
+
+==== tests/cases/conformance/salsa/a.js (1 errors) ====
+ ///
+ var mod1 = require('./mod1')
+ mod1.toFixed(12)
+ mod1.f() // error, 'f' is not a property on 'number'
+ ~
+!!! error TS2339: Property 'f' does not exist on type 'number'.
+
+==== tests/cases/conformance/salsa/requires.d.ts (0 errors) ====
+ declare var module: { exports: any };
+ declare function require(name: string): any;
+==== tests/cases/conformance/salsa/mod1.js (0 errors) ====
+ ///
+ module.exports = 1
+ module.exports.f = function () { }
+
\ No newline at end of file
diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment2.symbols b/tests/baselines/reference/moduleExportWithExportPropertyAssignment2.symbols
new file mode 100644
index 0000000000000..c9a09bb6a8b99
--- /dev/null
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment2.symbols
@@ -0,0 +1,37 @@
+=== tests/cases/conformance/salsa/a.js ===
+///
+var mod1 = require('./mod1')
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
+>require : Symbol(require, Decl(requires.d.ts, 0, 37))
+>'./mod1' : Symbol("tests/cases/conformance/salsa/mod1", Decl(mod1.js, 0, 0))
+
+mod1.toFixed(12)
+>mod1.toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --))
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
+>toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --))
+
+mod1.f() // error, 'f' is not a property on 'number'
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
+
+=== tests/cases/conformance/salsa/requires.d.ts ===
+declare var module: { exports: any };
+>module : Symbol(module, Decl(requires.d.ts, 0, 11))
+>exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
+
+declare function require(name: string): any;
+>require : Symbol(require, Decl(requires.d.ts, 0, 37))
+>name : Symbol(name, Decl(requires.d.ts, 1, 25))
+
+=== tests/cases/conformance/salsa/mod1.js ===
+///
+module.exports = 1
+>module.exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
+>module : Symbol(export=, Decl(mod1.js, 0, 0))
+>exports : Symbol(export=, Decl(mod1.js, 0, 0))
+
+module.exports.f = function () { }
+>module.exports : Symbol(f, Decl(mod1.js, 1, 18))
+>module : Symbol(module, Decl(requires.d.ts, 0, 11))
+>exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
+>f : Symbol(f, Decl(mod1.js, 1, 18))
+
diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment2.types b/tests/baselines/reference/moduleExportWithExportPropertyAssignment2.types
new file mode 100644
index 0000000000000..8af94a05fa5c6
--- /dev/null
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment2.types
@@ -0,0 +1,48 @@
+=== tests/cases/conformance/salsa/a.js ===
+///
+var mod1 = require('./mod1')
+>mod1 : number
+>require('./mod1') : number
+>require : (name: string) => any
+>'./mod1' : "./mod1"
+
+mod1.toFixed(12)
+>mod1.toFixed(12) : string
+>mod1.toFixed : (fractionDigits?: number) => string
+>mod1 : number
+>toFixed : (fractionDigits?: number) => string
+>12 : 12
+
+mod1.f() // error, 'f' is not a property on 'number'
+>mod1.f() : any
+>mod1.f : any
+>mod1 : number
+>f : any
+
+=== tests/cases/conformance/salsa/requires.d.ts ===
+declare var module: { exports: any };
+>module : { exports: any; }
+>exports : any
+
+declare function require(name: string): any;
+>require : (name: string) => any
+>name : string
+
+=== tests/cases/conformance/salsa/mod1.js ===
+///
+module.exports = 1
+>module.exports = 1 : 1
+>module.exports : any
+>module : { exports: any; }
+>exports : any
+>1 : 1
+
+module.exports.f = function () { }
+>module.exports.f = function () { } : () => void
+>module.exports.f : any
+>module.exports : any
+>module : { exports: any; }
+>exports : any
+>f : any
+>function () { } : () => void
+
diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment3.errors.txt b/tests/baselines/reference/moduleExportWithExportPropertyAssignment3.errors.txt
new file mode 100644
index 0000000000000..bbaed77a0f90c
--- /dev/null
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment3.errors.txt
@@ -0,0 +1,34 @@
+tests/cases/conformance/salsa/a.js(4,17): error TS2339: Property 'toFixed' does not exist on type 'string | number'.
+ Property 'toFixed' does not exist on type 'string'.
+tests/cases/conformance/salsa/a.js(5,16): error TS2339: Property 'toFixed' does not exist on type 'string | number'.
+ Property 'toFixed' does not exist on type 'string'.
+
+
+==== tests/cases/conformance/salsa/a.js (2 errors) ====
+ ///
+ var mod1 = require('./mod1')
+ mod1.justExport.toFixed()
+ mod1.bothBefore.toFixed() // error, 'toFixed' not on 'string | number'
+ ~~~~~~~
+!!! error TS2339: Property 'toFixed' does not exist on type 'string | number'.
+!!! error TS2339: Property 'toFixed' does not exist on type 'string'.
+ mod1.bothAfter.toFixed() // error, 'toFixed' not on 'string | number'
+ ~~~~~~~
+!!! error TS2339: Property 'toFixed' does not exist on type 'string | number'.
+!!! error TS2339: Property 'toFixed' does not exist on type 'string'.
+ mod1.justProperty.length
+
+==== tests/cases/conformance/salsa/requires.d.ts (0 errors) ====
+ declare var module: { exports: any };
+ declare function require(name: string): any;
+==== tests/cases/conformance/salsa/mod1.js (0 errors) ====
+ ///
+ module.exports.bothBefore = 'string'
+ module.exports = {
+ justExport: 1,
+ bothBefore: 2,
+ bothAfter: 3,
+ }
+ module.exports.bothAfter = 'string'
+ module.exports.justProperty = 'string'
+
\ No newline at end of file
diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment3.symbols b/tests/baselines/reference/moduleExportWithExportPropertyAssignment3.symbols
new file mode 100644
index 0000000000000..a84080cc998d8
--- /dev/null
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment3.symbols
@@ -0,0 +1,74 @@
+=== tests/cases/conformance/salsa/a.js ===
+///
+var mod1 = require('./mod1')
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
+>require : Symbol(require, Decl(requires.d.ts, 0, 37))
+>'./mod1' : Symbol("tests/cases/conformance/salsa/mod1", Decl(mod1.js, 0, 0))
+
+mod1.justExport.toFixed()
+>mod1.justExport.toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --))
+>mod1.justExport : Symbol(justExport, Decl(mod1.js, 2, 18))
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
+>justExport : Symbol(justExport, Decl(mod1.js, 2, 18))
+>toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --))
+
+mod1.bothBefore.toFixed() // error, 'toFixed' not on 'string | number'
+>mod1.bothBefore : Symbol(bothBefore)
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
+>bothBefore : Symbol(bothBefore)
+
+mod1.bothAfter.toFixed() // error, 'toFixed' not on 'string | number'
+>mod1.bothAfter : Symbol(bothAfter)
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
+>bothAfter : Symbol(bothAfter)
+
+mod1.justProperty.length
+>mod1.justProperty.length : Symbol(String.length, Decl(lib.d.ts, --, --))
+>mod1.justProperty : Symbol(justProperty, Decl(mod1.js, 7, 35))
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
+>justProperty : Symbol(justProperty, Decl(mod1.js, 7, 35))
+>length : Symbol(String.length, Decl(lib.d.ts, --, --))
+
+=== tests/cases/conformance/salsa/requires.d.ts ===
+declare var module: { exports: any };
+>module : Symbol(module, Decl(requires.d.ts, 0, 11))
+>exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
+
+declare function require(name: string): any;
+>require : Symbol(require, Decl(requires.d.ts, 0, 37))
+>name : Symbol(name, Decl(requires.d.ts, 1, 25))
+
+=== tests/cases/conformance/salsa/mod1.js ===
+///
+module.exports.bothBefore = 'string'
+>module.exports : Symbol(bothBefore, Decl(mod1.js, 0, 0))
+>module : Symbol(module, Decl(requires.d.ts, 0, 11))
+>exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
+>bothBefore : Symbol(bothBefore, Decl(mod1.js, 0, 0))
+
+module.exports = {
+>module.exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
+>module : Symbol(export=, Decl(mod1.js, 1, 36))
+>exports : Symbol(export=, Decl(mod1.js, 1, 36))
+
+ justExport: 1,
+>justExport : Symbol(justExport, Decl(mod1.js, 2, 18))
+
+ bothBefore: 2,
+>bothBefore : Symbol(bothBefore, Decl(mod1.js, 3, 18))
+
+ bothAfter: 3,
+>bothAfter : Symbol(bothAfter, Decl(mod1.js, 4, 18))
+}
+module.exports.bothAfter = 'string'
+>module.exports : Symbol(bothAfter, Decl(mod1.js, 6, 1))
+>module : Symbol(module, Decl(requires.d.ts, 0, 11))
+>exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
+>bothAfter : Symbol(bothAfter, Decl(mod1.js, 6, 1))
+
+module.exports.justProperty = 'string'
+>module.exports : Symbol(justProperty, Decl(mod1.js, 7, 35))
+>module : Symbol(module, Decl(requires.d.ts, 0, 11))
+>exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
+>justProperty : Symbol(justProperty, Decl(mod1.js, 7, 35))
+
diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment3.types b/tests/baselines/reference/moduleExportWithExportPropertyAssignment3.types
new file mode 100644
index 0000000000000..4d14bf647fd2c
--- /dev/null
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment3.types
@@ -0,0 +1,96 @@
+=== tests/cases/conformance/salsa/a.js ===
+///
+var mod1 = require('./mod1')
+>mod1 : { [x: string]: any; justExport: number; bothBefore: string | number; bothAfter: string | number; justProperty: string; }
+>require('./mod1') : { [x: string]: any; justExport: number; bothBefore: string | number; bothAfter: string | number; justProperty: string; }
+>require : (name: string) => any
+>'./mod1' : "./mod1"
+
+mod1.justExport.toFixed()
+>mod1.justExport.toFixed() : string
+>mod1.justExport.toFixed : (fractionDigits?: number) => string
+>mod1.justExport : number
+>mod1 : { [x: string]: any; justExport: number; bothBefore: string | number; bothAfter: string | number; justProperty: string; }
+>justExport : number
+>toFixed : (fractionDigits?: number) => string
+
+mod1.bothBefore.toFixed() // error, 'toFixed' not on 'string | number'
+>mod1.bothBefore.toFixed() : any
+>mod1.bothBefore.toFixed : any
+>mod1.bothBefore : string | number
+>mod1 : { [x: string]: any; justExport: number; bothBefore: string | number; bothAfter: string | number; justProperty: string; }
+>bothBefore : string | number
+>toFixed : any
+
+mod1.bothAfter.toFixed() // error, 'toFixed' not on 'string | number'
+>mod1.bothAfter.toFixed() : any
+>mod1.bothAfter.toFixed : any
+>mod1.bothAfter : string | number
+>mod1 : { [x: string]: any; justExport: number; bothBefore: string | number; bothAfter: string | number; justProperty: string; }
+>bothAfter : string | number
+>toFixed : any
+
+mod1.justProperty.length
+>mod1.justProperty.length : number
+>mod1.justProperty : string
+>mod1 : { [x: string]: any; justExport: number; bothBefore: string | number; bothAfter: string | number; justProperty: string; }
+>justProperty : string
+>length : number
+
+=== tests/cases/conformance/salsa/requires.d.ts ===
+declare var module: { exports: any };
+>module : { exports: any; }
+>exports : any
+
+declare function require(name: string): any;
+>require : (name: string) => any
+>name : string
+
+=== tests/cases/conformance/salsa/mod1.js ===
+///
+module.exports.bothBefore = 'string'
+>module.exports.bothBefore = 'string' : "string"
+>module.exports.bothBefore : any
+>module.exports : any
+>module : { exports: any; }
+>exports : any
+>bothBefore : any
+>'string' : "string"
+
+module.exports = {
+>module.exports = { justExport: 1, bothBefore: 2, bothAfter: 3,} : { [x: string]: any; justExport: number; bothBefore: number; bothAfter: number; }
+>module.exports : any
+>module : { exports: any; }
+>exports : any
+>{ justExport: 1, bothBefore: 2, bothAfter: 3,} : { [x: string]: any; justExport: number; bothBefore: number; bothAfter: number; }
+
+ justExport: 1,
+>justExport : number
+>1 : 1
+
+ bothBefore: 2,
+>bothBefore : number
+>2 : 2
+
+ bothAfter: 3,
+>bothAfter : number
+>3 : 3
+}
+module.exports.bothAfter = 'string'
+>module.exports.bothAfter = 'string' : "string"
+>module.exports.bothAfter : any
+>module.exports : any
+>module : { exports: any; }
+>exports : any
+>bothAfter : any
+>'string' : "string"
+
+module.exports.justProperty = 'string'
+>module.exports.justProperty = 'string' : "string"
+>module.exports.justProperty : any
+>module.exports : any
+>module : { exports: any; }
+>exports : any
+>justProperty : any
+>'string' : "string"
+
diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment4.errors.txt b/tests/baselines/reference/moduleExportWithExportPropertyAssignment4.errors.txt
new file mode 100644
index 0000000000000..247c29e0ad3af
--- /dev/null
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment4.errors.txt
@@ -0,0 +1,36 @@
+tests/cases/conformance/salsa/a.js(4,17): error TS2339: Property 'toFixed' does not exist on type 'string | number'.
+ Property 'toFixed' does not exist on type 'string'.
+tests/cases/conformance/salsa/a.js(5,16): error TS2339: Property 'toFixed' does not exist on type 'string | number'.
+ Property 'toFixed' does not exist on type 'string'.
+
+
+==== tests/cases/conformance/salsa/a.js (2 errors) ====
+ ///
+ var mod1 = require('./mod1')
+ mod1.justExport.toFixed()
+ mod1.bothBefore.toFixed() // error
+ ~~~~~~~
+!!! error TS2339: Property 'toFixed' does not exist on type 'string | number'.
+!!! error TS2339: Property 'toFixed' does not exist on type 'string'.
+ mod1.bothAfter.toFixed()
+ ~~~~~~~
+!!! error TS2339: Property 'toFixed' does not exist on type 'string | number'.
+!!! error TS2339: Property 'toFixed' does not exist on type 'string'.
+ mod1.justProperty.length
+
+==== tests/cases/conformance/salsa/requires.d.ts (0 errors) ====
+ declare var module: { exports: any };
+ declare function require(name: string): any;
+==== tests/cases/conformance/salsa/mod1.js (0 errors) ====
+ ///
+ module.exports.bothBefore = 'string'
+ A.justExport = 4
+ A.bothBefore = 2
+ A.bothAfter = 3
+ module.exports = A
+ function A() {
+ this.p = 1
+ }
+ module.exports.bothAfter = 'string'
+ module.exports.justProperty = 'string'
+
\ No newline at end of file
diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment4.symbols b/tests/baselines/reference/moduleExportWithExportPropertyAssignment4.symbols
new file mode 100644
index 0000000000000..b62a68c83f3c4
--- /dev/null
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment4.symbols
@@ -0,0 +1,87 @@
+=== tests/cases/conformance/salsa/a.js ===
+///
+var mod1 = require('./mod1')
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
+>require : Symbol(require, Decl(requires.d.ts, 0, 37))
+>'./mod1' : Symbol("tests/cases/conformance/salsa/mod1", Decl(mod1.js, 0, 0))
+
+mod1.justExport.toFixed()
+>mod1.justExport.toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --))
+>mod1.justExport : Symbol(A.justExport, Decl(mod1.js, 1, 36))
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
+>justExport : Symbol(A.justExport, Decl(mod1.js, 1, 36))
+>toFixed : Symbol(Number.toFixed, Decl(lib.d.ts, --, --))
+
+mod1.bothBefore.toFixed() // error
+>mod1.bothBefore : Symbol(bothBefore)
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
+>bothBefore : Symbol(bothBefore)
+
+mod1.bothAfter.toFixed()
+>mod1.bothAfter : Symbol(bothAfter)
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
+>bothAfter : Symbol(bothAfter)
+
+mod1.justProperty.length
+>mod1.justProperty.length : Symbol(String.length, Decl(lib.d.ts, --, --))
+>mod1.justProperty : Symbol(justProperty, Decl(mod1.js, 9, 35))
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
+>justProperty : Symbol(justProperty, Decl(mod1.js, 9, 35))
+>length : Symbol(String.length, Decl(lib.d.ts, --, --))
+
+=== tests/cases/conformance/salsa/requires.d.ts ===
+declare var module: { exports: any };
+>module : Symbol(module, Decl(requires.d.ts, 0, 11))
+>exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
+
+declare function require(name: string): any;
+>require : Symbol(require, Decl(requires.d.ts, 0, 37))
+>name : Symbol(name, Decl(requires.d.ts, 1, 25))
+
+=== tests/cases/conformance/salsa/mod1.js ===
+///
+module.exports.bothBefore = 'string'
+>module.exports : Symbol(bothBefore, Decl(mod1.js, 0, 0))
+>module : Symbol(module, Decl(requires.d.ts, 0, 11))
+>exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
+>bothBefore : Symbol(bothBefore, Decl(mod1.js, 0, 0))
+
+A.justExport = 4
+>A.justExport : Symbol(A.justExport, Decl(mod1.js, 1, 36))
+>A : Symbol(A, Decl(mod1.js, 5, 18))
+>justExport : Symbol(A.justExport, Decl(mod1.js, 1, 36))
+
+A.bothBefore = 2
+>A.bothBefore : Symbol(A.bothBefore, Decl(mod1.js, 2, 16))
+>A : Symbol(A, Decl(mod1.js, 5, 18))
+>bothBefore : Symbol(A.bothBefore, Decl(mod1.js, 2, 16))
+
+A.bothAfter = 3
+>A.bothAfter : Symbol(A.bothAfter, Decl(mod1.js, 3, 16))
+>A : Symbol(A, Decl(mod1.js, 5, 18))
+>bothAfter : Symbol(A.bothAfter, Decl(mod1.js, 3, 16))
+
+module.exports = A
+>module.exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
+>module : Symbol(export=, Decl(mod1.js, 4, 15))
+>exports : Symbol(export=, Decl(mod1.js, 4, 15))
+>A : Symbol(A, Decl(mod1.js, 5, 18))
+
+function A() {
+>A : Symbol(A, Decl(mod1.js, 5, 18))
+
+ this.p = 1
+>p : Symbol(A.p, Decl(mod1.js, 6, 14))
+}
+module.exports.bothAfter = 'string'
+>module.exports : Symbol(bothAfter, Decl(mod1.js, 8, 1))
+>module : Symbol(module, Decl(requires.d.ts, 0, 11))
+>exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
+>bothAfter : Symbol(bothAfter, Decl(mod1.js, 8, 1))
+
+module.exports.justProperty = 'string'
+>module.exports : Symbol(justProperty, Decl(mod1.js, 9, 35))
+>module : Symbol(module, Decl(requires.d.ts, 0, 11))
+>exports : Symbol(exports, Decl(requires.d.ts, 0, 21))
+>justProperty : Symbol(justProperty, Decl(mod1.js, 9, 35))
+
diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment4.types b/tests/baselines/reference/moduleExportWithExportPropertyAssignment4.types
new file mode 100644
index 0000000000000..f339118bf05ac
--- /dev/null
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment4.types
@@ -0,0 +1,115 @@
+=== tests/cases/conformance/salsa/a.js ===
+///
+var mod1 = require('./mod1')
+>mod1 : typeof A
+>require('./mod1') : typeof A
+>require : (name: string) => any
+>'./mod1' : "./mod1"
+
+mod1.justExport.toFixed()
+>mod1.justExport.toFixed() : string
+>mod1.justExport.toFixed : (fractionDigits?: number) => string
+>mod1.justExport : number
+>mod1 : typeof A
+>justExport : number
+>toFixed : (fractionDigits?: number) => string
+
+mod1.bothBefore.toFixed() // error
+>mod1.bothBefore.toFixed() : any
+>mod1.bothBefore.toFixed : any
+>mod1.bothBefore : string | number
+>mod1 : typeof A
+>bothBefore : string | number
+>toFixed : any
+
+mod1.bothAfter.toFixed()
+>mod1.bothAfter.toFixed() : any
+>mod1.bothAfter.toFixed : any
+>mod1.bothAfter : string | number
+>mod1 : typeof A
+>bothAfter : string | number
+>toFixed : any
+
+mod1.justProperty.length
+>mod1.justProperty.length : number
+>mod1.justProperty : string
+>mod1 : typeof A
+>justProperty : string
+>length : number
+
+=== tests/cases/conformance/salsa/requires.d.ts ===
+declare var module: { exports: any };
+>module : { exports: any; }
+>exports : any
+
+declare function require(name: string): any;
+>require : (name: string) => any
+>name : string
+
+=== tests/cases/conformance/salsa/mod1.js ===
+///
+module.exports.bothBefore = 'string'
+>module.exports.bothBefore = 'string' : "string"
+>module.exports.bothBefore : any
+>module.exports : any
+>module : { exports: any; }
+>exports : any
+>bothBefore : any
+>'string' : "string"
+
+A.justExport = 4
+>A.justExport = 4 : 4
+>A.justExport : number
+>A : typeof A
+>justExport : number
+>4 : 4
+
+A.bothBefore = 2
+>A.bothBefore = 2 : 2
+>A.bothBefore : number
+>A : typeof A
+>bothBefore : number
+>2 : 2
+
+A.bothAfter = 3
+>A.bothAfter = 3 : 3
+>A.bothAfter : number
+>A : typeof A
+>bothAfter : number
+>3 : 3
+
+module.exports = A
+>module.exports = A : typeof A
+>module.exports : any
+>module : { exports: any; }
+>exports : any
+>A : typeof A
+
+function A() {
+>A : typeof A
+
+ this.p = 1
+>this.p = 1 : 1
+>this.p : any
+>this : any
+>p : any
+>1 : 1
+}
+module.exports.bothAfter = 'string'
+>module.exports.bothAfter = 'string' : "string"
+>module.exports.bothAfter : any
+>module.exports : any
+>module : { exports: any; }
+>exports : any
+>bothAfter : any
+>'string' : "string"
+
+module.exports.justProperty = 'string'
+>module.exports.justProperty = 'string' : "string"
+>module.exports.justProperty : any
+>module.exports : any
+>module : { exports: any; }
+>exports : any
+>justProperty : any
+>'string' : "string"
+
diff --git a/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment.ts b/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment.ts
index 9486f238e74e4..926a4cadf08f7 100644
--- a/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment.ts
+++ b/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment.ts
@@ -1,12 +1,17 @@
// @noEmit: true
// @allowJs: true
// @checkJs: true
+// @Filename: requires.d.ts
+declare var module: { exports: any };
+declare function require(name: string): any;
// @Filename: mod1.js
-// TODO: Test that this is not legal (or at least doesn't work) if module.exports is a primitive
+///
module.exports = function () { }
-module.exports.f = function () { }
+/** @param {number} a */
+module.exports.f = function (a) { }
// @Filename: a.js
+///
var mod1 = require('./mod1')
mod1()
-mod1.f()
+mod1.f() // error, not enough arguments
diff --git a/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment2.ts b/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment2.ts
new file mode 100644
index 0000000000000..ae3d770b0a412
--- /dev/null
+++ b/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment2.ts
@@ -0,0 +1,16 @@
+// @noEmit: true
+// @allowJs: true
+// @checkJs: true
+// @Filename: requires.d.ts
+declare var module: { exports: any };
+declare function require(name: string): any;
+// @Filename: mod1.js
+///
+module.exports = 1
+module.exports.f = function () { }
+
+// @Filename: a.js
+///
+var mod1 = require('./mod1')
+mod1.toFixed(12)
+mod1.f() // error, 'f' is not a property on 'number'
diff --git a/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment3.ts b/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment3.ts
new file mode 100644
index 0000000000000..673858bb13e4f
--- /dev/null
+++ b/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment3.ts
@@ -0,0 +1,24 @@
+// @noEmit: true
+// @allowJs: true
+// @checkJs: true
+// @Filename: requires.d.ts
+declare var module: { exports: any };
+declare function require(name: string): any;
+// @Filename: mod1.js
+///
+module.exports.bothBefore = 'string'
+module.exports = {
+ justExport: 1,
+ bothBefore: 2,
+ bothAfter: 3,
+}
+module.exports.bothAfter = 'string'
+module.exports.justProperty = 'string'
+
+// @Filename: a.js
+///
+var mod1 = require('./mod1')
+mod1.justExport.toFixed()
+mod1.bothBefore.toFixed() // error, 'toFixed' not on 'string | number'
+mod1.bothAfter.toFixed() // error, 'toFixed' not on 'string | number'
+mod1.justProperty.length
diff --git a/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment4.ts b/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment4.ts
new file mode 100644
index 0000000000000..90b54095cbf11
--- /dev/null
+++ b/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment4.ts
@@ -0,0 +1,26 @@
+// @noEmit: true
+// @allowJs: true
+// @checkJs: true
+// @Filename: requires.d.ts
+declare var module: { exports: any };
+declare function require(name: string): any;
+// @Filename: mod1.js
+///
+module.exports.bothBefore = 'string'
+A.justExport = 4
+A.bothBefore = 2
+A.bothAfter = 3
+module.exports = A
+function A() {
+ this.p = 1
+}
+module.exports.bothAfter = 'string'
+module.exports.justProperty = 'string'
+
+// @Filename: a.js
+///
+var mod1 = require('./mod1')
+mod1.justExport.toFixed()
+mod1.bothBefore.toFixed() // error
+mod1.bothAfter.toFixed()
+mod1.justProperty.length
From c6a47d97fcc3bc0e6be6b0f17426e37d467524a4 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Fri, 6 Apr 2018 12:34:17 -0700
Subject: [PATCH 5/5] Check commonjs export= from resolveExternalModuleSymbol
---
src/compiler/checker.ts | 45 +++++++++++++++++------------------------
1 file changed, 19 insertions(+), 26 deletions(-)
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index a33421c946dd5..f3d178e9478fa 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -2096,15 +2096,9 @@ namespace ts {
const moduleName = (namespace.valueDeclaration.initializer as CallExpression).arguments[0] as StringLiteral;
const moduleSym = resolveExternalModuleName(moduleName, moduleName);
if (moduleSym) {
- if (moduleSym.exports.has(InternalSymbolName.ExportEquals as __String) && moduleSym.exports.size > 1) {
- Debug.assert(!!(moduleSym.valueDeclaration as SourceFile).commonJsModuleIndicator);
- namespace = mergeCommonJsExportEquals(moduleSym);
- }
- else {
- const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym);
- if (resolvedModuleSymbol) {
- namespace = resolvedModuleSymbol;
- }
+ const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym);
+ if (resolvedModuleSymbol) {
+ namespace = resolvedModuleSymbol;
}
}
}
@@ -2222,7 +2216,22 @@ namespace ts {
// An external module with an 'export =' declaration resolves to the target of the 'export =' declaration,
// and an external module with no 'export =' declaration resolves to the module itself.
function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol {
- return moduleSymbol && getMergedSymbol(resolveSymbol(moduleSymbol.exports.get(InternalSymbolName.ExportEquals), dontResolveAlias)) || moduleSymbol;
+ return moduleSymbol && getMergedSymbol(resolveSymbol(getCommonJsExportEquals(moduleSymbol), dontResolveAlias)) || moduleSymbol;
+ }
+
+ function getCommonJsExportEquals(moduleSymbol: Symbol): Symbol {
+ const exported = moduleSymbol.exports.get(InternalSymbolName.ExportEquals);
+ if (!exported || !exported.exports || moduleSymbol.exports.size === 1) {
+ return exported;
+ }
+ const merged = cloneSymbol(exported);
+ moduleSymbol.exports.forEach((s, name) => {
+ if (name === InternalSymbolName.ExportEquals) return;
+ if (!merged.exports.has(name)) {
+ merged.exports.set(name, s);
+ }
+ });
+ return merged;
}
// An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export ='
@@ -7025,10 +7034,6 @@ namespace ts {
function resolveExternalModuleTypeByLiteral(name: StringLiteral) {
const moduleSym = resolveExternalModuleName(name, name);
if (moduleSym) {
- if (moduleSym.exports.has(InternalSymbolName.ExportEquals as __String) && moduleSym.exports.size > 1) {
- Debug.assert(!!(moduleSym.valueDeclaration as SourceFile).commonJsModuleIndicator);
- return getTypeOfSymbol(mergeCommonJsExportEquals(moduleSym));
- }
const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym);
if (resolvedModuleSymbol) {
return getTypeOfSymbol(resolvedModuleSymbol);
@@ -7038,18 +7043,6 @@ namespace ts {
return anyType;
}
- function mergeCommonJsExportEquals(moduleSym: Symbol): Symbol {
- const combined = cloneSymbol(moduleSym.exports.get(InternalSymbolName.ExportEquals));
- moduleSym.exports.forEach((s, name) => {
- if (name === InternalSymbolName.ExportEquals) return;
- if (!combined.exports.has(name)) {
- combined.exports.set(name, s);
- }
- });
- return combined;
-
- }
-
function getThisTypeOfSignature(signature: Signature): Type | undefined {
if (signature.thisParameter) {
return getTypeOfSymbol(signature.thisParameter);