diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 34e5000790add..f3d178e9478fa 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -2216,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 ='
@@ -4350,7 +4365,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 +4396,33 @@ 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 exportedType = resolveStructuredTypeMembers(type as AnonymousType);
+ const members = createSymbolTable();
+ 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)) {
anyedType = anyArrayType;
@@ -24568,7 +24610,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/baselines/reference/moduleExportWithExportPropertyAssignment.errors.txt b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.errors.txt
new file mode 100644
index 0000000000000..aab334e77f38c
--- /dev/null
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.errors.txt
@@ -0,0 +1,20 @@
+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')
+ mod1()
+ mod1.f() // error, not enough arguments
+ ~~~~~~~~
+!!! error TS2554: Expected 1 arguments, but got 0.
+
+==== 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 () { }
+ /** @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
new file mode 100644
index 0000000000000..c5120a80571e3
--- /dev/null
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.symbols
@@ -0,0 +1,39 @@
+=== 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()
+>mod1 : Symbol(mod1, Decl(a.js, 1, 3))
+
+mod1.f() // error, not enough arguments
+>mod1.f : Symbol(f, Decl(mod1.js, 1, 32))
+>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 ===
+///
+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))
+
+/** @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
new file mode 100644
index 0000000000000..b7170842c1f39
--- /dev/null
+++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment.types
@@ -0,0 +1,47 @@
+=== tests/cases/conformance/salsa/a.js ===
+///
+var mod1 = require('./mod1')
+>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: (a: number) => void; }
+
+mod1.f() // error, not enough arguments
+>mod1.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 ===
+///
+module.exports = function () { }
+>module.exports = function () { } : () => void
+>module.exports : any
+>module : { exports: any; }
+>exports : any
+>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 : { exports: any; }
+>exports : any
+>f : any
+>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
new file mode 100644
index 0000000000000..926a4cadf08f7
--- /dev/null
+++ b/tests/cases/conformance/salsa/moduleExportWithExportPropertyAssignment.ts
@@ -0,0 +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
+///
+module.exports = function () { }
+/** @param {number} a */
+module.exports.f = function (a) { }
+
+// @Filename: a.js
+///
+var mod1 = require('./mod1')
+mod1()
+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