diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7835a5e3e72..5a8ecc2e83d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1125,6 +1125,13 @@ namespace ts { } if (!result) { + if (lastLocation) { + Debug.assert(lastLocation.kind === SyntaxKind.SourceFile); + if ((lastLocation as SourceFile).commonJsModuleIndicator && name === "exports") { + return lastLocation.symbol; + } + } + result = lookup(globals, name, meaning); } @@ -12885,7 +12892,8 @@ namespace ts { } } } - if (noImplicitThis || isInJavaScriptFile(func)) { + const inJs = isInJavaScriptFile(func); + if (noImplicitThis || inJs) { const containingLiteral = getContainingObjectLiteral(func); if (containingLiteral) { // We have an object literal method. Check if the containing object literal has a contextual type @@ -12912,10 +12920,20 @@ namespace ts { } // In an assignment of the form 'obj.xxx = function(...)' or 'obj[xxx] = function(...)', the // contextual type for 'this' is 'obj'. - if (func.parent.kind === SyntaxKind.BinaryExpression && (func.parent).operatorToken.kind === SyntaxKind.EqualsToken) { - const target = (func.parent).left; + const { parent } = func; + if (parent.kind === SyntaxKind.BinaryExpression && (parent).operatorToken.kind === SyntaxKind.EqualsToken) { + const target = (parent).left; if (target.kind === SyntaxKind.PropertyAccessExpression || target.kind === SyntaxKind.ElementAccessExpression) { - return checkExpressionCached((target).expression); + const { expression } = target as PropertyAccessExpression | ElementAccessExpression; + // Don't contextually type `this` as `exports` in `exports.Point = function(x, y) { this.x = x; this.y = y; }` + if (inJs && isIdentifier(expression)) { + const sourceFile = getSourceFileOfNode(parent); + if (sourceFile.commonJsModuleIndicator && getResolvedSymbol(expression) === sourceFile.symbol) { + return undefined; + } + } + + return checkExpressionCached(expression); } } } diff --git a/tests/baselines/reference/commonjsAccessExports.symbols b/tests/baselines/reference/commonjsAccessExports.symbols new file mode 100644 index 00000000000..dce61803408 --- /dev/null +++ b/tests/baselines/reference/commonjsAccessExports.symbols @@ -0,0 +1,30 @@ +=== /a.js === +exports.x = 0; +>exports.x : Symbol(x, Decl(a.js, 0, 0)) +>exports : Symbol(x, Decl(a.js, 0, 0)) +>x : Symbol(x, Decl(a.js, 0, 0)) + +exports.x; +>exports.x : Symbol(x, Decl(a.js, 0, 0)) +>exports : Symbol("/a", Decl(a.js, 0, 0)) +>x : Symbol(x, Decl(a.js, 0, 0)) + +// Works nested +{ + // 'exports' does not provide a contextual type to a function-class + exports.Cls = function() { +>exports.Cls : Symbol(Cls, Decl(a.js, 4, 1)) +>exports : Symbol(Cls, Decl(a.js, 4, 1)) +>Cls : Symbol(Cls, Decl(a.js, 4, 1)) + + this.x = 0; +>x : Symbol((Anonymous function).x, Decl(a.js, 6, 30)) + } +} + +const instance = new exports.Cls(); +>instance : Symbol(instance, Decl(a.js, 11, 5)) +>exports.Cls : Symbol(Cls, Decl(a.js, 4, 1)) +>exports : Symbol("/a", Decl(a.js, 0, 0)) +>Cls : Symbol(Cls, Decl(a.js, 4, 1)) + diff --git a/tests/baselines/reference/commonjsAccessExports.types b/tests/baselines/reference/commonjsAccessExports.types new file mode 100644 index 00000000000..61d57d4b2cc --- /dev/null +++ b/tests/baselines/reference/commonjsAccessExports.types @@ -0,0 +1,39 @@ +=== /a.js === +exports.x = 0; +>exports.x = 0 : 0 +>exports.x : number +>exports : typeof "/a" +>x : number +>0 : 0 + +exports.x; +>exports.x : number +>exports : typeof "/a" +>x : number + +// Works nested +{ + // 'exports' does not provide a contextual type to a function-class + exports.Cls = function() { +>exports.Cls = function() { this.x = 0; } : () => void +>exports.Cls : () => void +>exports : typeof "/a" +>Cls : () => void +>function() { this.x = 0; } : () => void + + this.x = 0; +>this.x = 0 : 0 +>this.x : any +>this : any +>x : any +>0 : 0 + } +} + +const instance = new exports.Cls(); +>instance : { x: number; } +>new exports.Cls() : { x: number; } +>exports.Cls : () => void +>exports : typeof "/a" +>Cls : () => void + diff --git a/tests/baselines/reference/jsFileCompilationExternalPackageError.symbols b/tests/baselines/reference/jsFileCompilationExternalPackageError.symbols index 7663b2fcedf..ed03884c653 100644 --- a/tests/baselines/reference/jsFileCompilationExternalPackageError.symbols +++ b/tests/baselines/reference/jsFileCompilationExternalPackageError.symbols @@ -17,6 +17,7 @@ var a = 10; === tests/cases/compiler/node_modules/c.js === exports.a = 10; +>exports.a : Symbol(a, Decl(c.js, 0, 0)) >exports : Symbol(a, Decl(c.js, 0, 0)) >a : Symbol(a, Decl(c.js, 0, 0)) diff --git a/tests/baselines/reference/jsFileCompilationExternalPackageError.types b/tests/baselines/reference/jsFileCompilationExternalPackageError.types index c41a24fcffd..82418aeeb28 100644 --- a/tests/baselines/reference/jsFileCompilationExternalPackageError.types +++ b/tests/baselines/reference/jsFileCompilationExternalPackageError.types @@ -21,9 +21,9 @@ var a = 10; === tests/cases/compiler/node_modules/c.js === exports.a = 10; >exports.a = 10 : 10 ->exports.a : any ->exports : any ->a : any +>exports.a : number +>exports : typeof "tests/cases/compiler/node_modules/c" +>a : number >10 : 10 c = 10; diff --git a/tests/baselines/reference/moduleExportAlias.symbols b/tests/baselines/reference/moduleExportAlias.symbols index ed480a0033c..9e2e0e554d2 100644 --- a/tests/baselines/reference/moduleExportAlias.symbols +++ b/tests/baselines/reference/moduleExportAlias.symbols @@ -106,11 +106,15 @@ b.func20; === tests/cases/conformance/salsa/b.js === var exportsAlias = exports; >exportsAlias : Symbol(exportsAlias, Decl(b.js, 0, 3)) +>exports : Symbol("tests/cases/conformance/salsa/b", Decl(b.js, 0, 0)) exportsAlias.func1 = function () { }; +>exportsAlias.func1 : Symbol(func1, Decl(b.js, 0, 27)) >exportsAlias : Symbol(exportsAlias, Decl(b.js, 0, 3)) +>func1 : Symbol(func1, Decl(b.js, 0, 27)) exports.func2 = function () { }; +>exports.func2 : Symbol(func2, Decl(b.js, 1, 37)) >exports : Symbol(func2, Decl(b.js, 1, 37)) >func2 : Symbol(func2, Decl(b.js, 1, 37)) @@ -126,15 +130,19 @@ module.exports.func4 = function () { }; var multipleDeclarationAlias1 = exports = module.exports; >multipleDeclarationAlias1 : Symbol(multipleDeclarationAlias1, Decl(b.js, 8, 3)) +>exports : Symbol("tests/cases/conformance/salsa/b", Decl(b.js, 0, 0)) multipleDeclarationAlias1.func5 = function () { }; >multipleDeclarationAlias1 : Symbol(multipleDeclarationAlias1, Decl(b.js, 8, 3)) var multipleDeclarationAlias2 = module.exports = exports; >multipleDeclarationAlias2 : Symbol(multipleDeclarationAlias2, Decl(b.js, 11, 3)) +>exports : Symbol("tests/cases/conformance/salsa/b", Decl(b.js, 0, 0)) multipleDeclarationAlias2.func6 = function () { }; +>multipleDeclarationAlias2.func6 : Symbol(func6, Decl(b.js, 11, 57)) >multipleDeclarationAlias2 : Symbol(multipleDeclarationAlias2, Decl(b.js, 11, 3)) +>func6 : Symbol(func6, Decl(b.js, 11, 57)) var someOtherVariable; >someOtherVariable : Symbol(someOtherVariable, Decl(b.js, 14, 3)) @@ -142,9 +150,12 @@ var someOtherVariable; var multipleDeclarationAlias3 = someOtherVariable = exports; >multipleDeclarationAlias3 : Symbol(multipleDeclarationAlias3, Decl(b.js, 15, 3)) >someOtherVariable : Symbol(someOtherVariable, Decl(b.js, 14, 3)) +>exports : Symbol("tests/cases/conformance/salsa/b", Decl(b.js, 0, 0)) multipleDeclarationAlias3.func7 = function () { }; +>multipleDeclarationAlias3.func7 : Symbol(func7, Decl(b.js, 15, 60)) >multipleDeclarationAlias3 : Symbol(multipleDeclarationAlias3, Decl(b.js, 15, 3)) +>func7 : Symbol(func7, Decl(b.js, 15, 60)) var multipleDeclarationAlias4 = someOtherVariable = module.exports; >multipleDeclarationAlias4 : Symbol(multipleDeclarationAlias4, Decl(b.js, 18, 3)) @@ -155,20 +166,24 @@ multipleDeclarationAlias4.func8 = function () { }; var multipleDeclarationAlias5 = module.exports = exports = {}; >multipleDeclarationAlias5 : Symbol(multipleDeclarationAlias5, Decl(b.js, 21, 3)) +>exports : Symbol("tests/cases/conformance/salsa/b", Decl(b.js, 0, 0)) multipleDeclarationAlias5.func9 = function () { }; >multipleDeclarationAlias5 : Symbol(multipleDeclarationAlias5, Decl(b.js, 21, 3)) var multipleDeclarationAlias6 = exports = module.exports = {}; >multipleDeclarationAlias6 : Symbol(multipleDeclarationAlias6, Decl(b.js, 24, 3)) +>exports : Symbol("tests/cases/conformance/salsa/b", Decl(b.js, 0, 0)) multipleDeclarationAlias6.func10 = function () { }; >multipleDeclarationAlias6 : Symbol(multipleDeclarationAlias6, Decl(b.js, 24, 3)) exports = module.exports = someOtherVariable = {}; +>exports : Symbol("tests/cases/conformance/salsa/b", Decl(b.js, 0, 0)) >someOtherVariable : Symbol(someOtherVariable, Decl(b.js, 14, 3)) exports.func11 = function () { }; +>exports.func11 : Symbol(func11, Decl(b.js, 27, 50), Decl(b.js, 31, 50)) >exports : Symbol(func11, Decl(b.js, 27, 50), Decl(b.js, 31, 50)) >func11 : Symbol(func11, Decl(b.js, 27, 50), Decl(b.js, 31, 50)) @@ -177,9 +192,11 @@ module.exports.func12 = function () { }; >func12 : Symbol(func12, Decl(b.js, 28, 33), Decl(b.js, 32, 33)) exports = module.exports = someOtherVariable = {}; +>exports : Symbol("tests/cases/conformance/salsa/b", Decl(b.js, 0, 0)) >someOtherVariable : Symbol(someOtherVariable, Decl(b.js, 14, 3)) exports.func11 = function () { }; +>exports.func11 : Symbol(func11, Decl(b.js, 27, 50), Decl(b.js, 31, 50)) >exports : Symbol(func11, Decl(b.js, 27, 50), Decl(b.js, 31, 50)) >func11 : Symbol(func11, Decl(b.js, 27, 50), Decl(b.js, 31, 50)) @@ -188,7 +205,10 @@ module.exports.func12 = function () { }; >func12 : Symbol(func12, Decl(b.js, 28, 33), Decl(b.js, 32, 33)) exports = module.exports = {}; +>exports : Symbol("tests/cases/conformance/salsa/b", Decl(b.js, 0, 0)) + exports.func13 = function () { }; +>exports.func13 : Symbol(func13, Decl(b.js, 35, 30)) >exports : Symbol(func13, Decl(b.js, 35, 30)) >func13 : Symbol(func13, Decl(b.js, 35, 30)) @@ -197,7 +217,10 @@ module.exports.func14 = function () { }; >func14 : Symbol(func14, Decl(b.js, 36, 33)) exports = module.exports = {}; +>exports : Symbol("tests/cases/conformance/salsa/b", Decl(b.js, 0, 0)) + exports.func15 = function () { }; +>exports.func15 : Symbol(func15, Decl(b.js, 39, 30)) >exports : Symbol(func15, Decl(b.js, 39, 30)) >func15 : Symbol(func15, Decl(b.js, 39, 30)) @@ -206,7 +229,10 @@ module.exports.func16 = function () { }; >func16 : Symbol(func16, Decl(b.js, 40, 33)) module.exports = exports = {}; +>exports : Symbol("tests/cases/conformance/salsa/b", Decl(b.js, 0, 0)) + exports.func17 = function () { }; +>exports.func17 : Symbol(func17, Decl(b.js, 43, 30)) >exports : Symbol(func17, Decl(b.js, 43, 30)) >func17 : Symbol(func17, Decl(b.js, 43, 30)) @@ -216,6 +242,7 @@ module.exports.func18 = function () { }; module.exports = {}; exports.func19 = function () { }; +>exports.func19 : Symbol(func19, Decl(b.js, 47, 20)) >exports : Symbol(func19, Decl(b.js, 47, 20)) >func19 : Symbol(func19, Decl(b.js, 47, 20)) diff --git a/tests/baselines/reference/moduleExportAlias.types b/tests/baselines/reference/moduleExportAlias.types index ccf8e5d7a24..b1eedf8c2ab 100644 --- a/tests/baselines/reference/moduleExportAlias.types +++ b/tests/baselines/reference/moduleExportAlias.types @@ -105,21 +105,21 @@ b.func20; === tests/cases/conformance/salsa/b.js === var exportsAlias = exports; ->exportsAlias : any ->exports : any +>exportsAlias : typeof "tests/cases/conformance/salsa/b" +>exports : typeof "tests/cases/conformance/salsa/b" exportsAlias.func1 = function () { }; >exportsAlias.func1 = function () { } : () => void ->exportsAlias.func1 : any ->exportsAlias : any ->func1 : any +>exportsAlias.func1 : () => void +>exportsAlias : typeof "tests/cases/conformance/salsa/b" +>func1 : () => void >function () { } : () => void exports.func2 = function () { }; >exports.func2 = function () { } : () => void ->exports.func2 : any ->exports : any ->func2 : any +>exports.func2 : () => void +>exports : typeof "tests/cases/conformance/salsa/b" +>func2 : () => void >function () { } : () => void var moduleExportsAlias = module.exports; @@ -160,34 +160,34 @@ multipleDeclarationAlias1.func5 = function () { }; >function () { } : () => void var multipleDeclarationAlias2 = module.exports = exports; ->multipleDeclarationAlias2 : any ->module.exports = exports : any +>multipleDeclarationAlias2 : typeof "tests/cases/conformance/salsa/b" +>module.exports = exports : typeof "tests/cases/conformance/salsa/b" >module.exports : any >module : any >exports : any ->exports : any +>exports : typeof "tests/cases/conformance/salsa/b" multipleDeclarationAlias2.func6 = function () { }; >multipleDeclarationAlias2.func6 = function () { } : () => void ->multipleDeclarationAlias2.func6 : any ->multipleDeclarationAlias2 : any ->func6 : any +>multipleDeclarationAlias2.func6 : () => void +>multipleDeclarationAlias2 : typeof "tests/cases/conformance/salsa/b" +>func6 : () => void >function () { } : () => void var someOtherVariable; >someOtherVariable : any var multipleDeclarationAlias3 = someOtherVariable = exports; ->multipleDeclarationAlias3 : any ->someOtherVariable = exports : any +>multipleDeclarationAlias3 : typeof "tests/cases/conformance/salsa/b" +>someOtherVariable = exports : typeof "tests/cases/conformance/salsa/b" >someOtherVariable : any ->exports : any +>exports : typeof "tests/cases/conformance/salsa/b" multipleDeclarationAlias3.func7 = function () { }; >multipleDeclarationAlias3.func7 = function () { } : () => void ->multipleDeclarationAlias3.func7 : any ->multipleDeclarationAlias3 : any ->func7 : any +>multipleDeclarationAlias3.func7 : () => void +>multipleDeclarationAlias3 : typeof "tests/cases/conformance/salsa/b" +>func7 : () => void >function () { } : () => void var multipleDeclarationAlias4 = someOtherVariable = module.exports; @@ -252,9 +252,9 @@ exports = module.exports = someOtherVariable = {}; exports.func11 = function () { }; >exports.func11 = function () { } : () => void ->exports.func11 : any ->exports : any ->func11 : any +>exports.func11 : () => void +>exports : typeof "tests/cases/conformance/salsa/b" +>func11 : () => void >function () { } : () => void module.exports.func12 = function () { }; @@ -279,9 +279,9 @@ exports = module.exports = someOtherVariable = {}; exports.func11 = function () { }; >exports.func11 = function () { } : () => void ->exports.func11 : any ->exports : any ->func11 : any +>exports.func11 : () => void +>exports : typeof "tests/cases/conformance/salsa/b" +>func11 : () => void >function () { } : () => void module.exports.func12 = function () { }; @@ -304,9 +304,9 @@ exports = module.exports = {}; exports.func13 = function () { }; >exports.func13 = function () { } : () => void ->exports.func13 : any ->exports : any ->func13 : any +>exports.func13 : () => void +>exports : typeof "tests/cases/conformance/salsa/b" +>func13 : () => void >function () { } : () => void module.exports.func14 = function () { }; @@ -329,9 +329,9 @@ exports = module.exports = {}; exports.func15 = function () { }; >exports.func15 = function () { } : () => void ->exports.func15 : any ->exports : any ->func15 : any +>exports.func15 : () => void +>exports : typeof "tests/cases/conformance/salsa/b" +>func15 : () => void >function () { } : () => void module.exports.func16 = function () { }; @@ -354,9 +354,9 @@ module.exports = exports = {}; exports.func17 = function () { }; >exports.func17 = function () { } : () => void ->exports.func17 : any ->exports : any ->func17 : any +>exports.func17 : () => void +>exports : typeof "tests/cases/conformance/salsa/b" +>func17 : () => void >function () { } : () => void module.exports.func18 = function () { }; @@ -377,9 +377,9 @@ module.exports = {}; exports.func19 = function () { }; >exports.func19 = function () { } : () => void ->exports.func19 : any ->exports : any ->func19 : any +>exports.func19 : () => void +>exports : typeof "tests/cases/conformance/salsa/b" +>func19 : () => void >function () { } : () => void module.exports.func20 = function () { }; diff --git a/tests/baselines/reference/moduleResolution_explicitNodeModulesImport.symbols b/tests/baselines/reference/moduleResolution_explicitNodeModulesImport.symbols index b635ba2c7c7..b091b2d4ab2 100644 --- a/tests/baselines/reference/moduleResolution_explicitNodeModulesImport.symbols +++ b/tests/baselines/reference/moduleResolution_explicitNodeModulesImport.symbols @@ -4,6 +4,7 @@ import { x } from "../node_modules/foo"; === /node_modules/foo/index.js === exports.x = 0; +>exports.x : Symbol(x, Decl(index.js, 0, 0)) >exports : Symbol(x, Decl(index.js, 0, 0)) >x : Symbol(x, Decl(index.js, 0, 0)) diff --git a/tests/baselines/reference/moduleResolution_explicitNodeModulesImport.types b/tests/baselines/reference/moduleResolution_explicitNodeModulesImport.types index ca7541d2140..fe7d74c914c 100644 --- a/tests/baselines/reference/moduleResolution_explicitNodeModulesImport.types +++ b/tests/baselines/reference/moduleResolution_explicitNodeModulesImport.types @@ -5,8 +5,8 @@ import { x } from "../node_modules/foo"; === /node_modules/foo/index.js === exports.x = 0; >exports.x = 0 : 0 ->exports.x : any ->exports : any ->x : any +>exports.x : number +>exports : typeof "/node_modules/foo/index" +>x : number >0 : 0 diff --git a/tests/baselines/reference/typeFromParamTagForFunction.symbols b/tests/baselines/reference/typeFromParamTagForFunction.symbols index 0df0dfdc206..5bacb93cc7a 100644 --- a/tests/baselines/reference/typeFromParamTagForFunction.symbols +++ b/tests/baselines/reference/typeFromParamTagForFunction.symbols @@ -9,6 +9,7 @@ declare var module: any, exports: any; === tests/cases/conformance/salsa/a-ext.js === exports.A = function () { +>exports.A : Symbol(A, Decl(a-ext.js, 0, 0)) >exports : Symbol(A, Decl(a-ext.js, 0, 0)) >A : Symbol(A, Decl(a-ext.js, 0, 0)) @@ -33,6 +34,7 @@ function a(p) { p.x; } === tests/cases/conformance/salsa/b-ext.js === exports.B = class { +>exports.B : Symbol(B, Decl(b-ext.js, 0, 0)) >exports : Symbol(B, Decl(b-ext.js, 0, 0)) >B : Symbol(B, Decl(b-ext.js, 0, 0)) diff --git a/tests/baselines/reference/typeFromParamTagForFunction.types b/tests/baselines/reference/typeFromParamTagForFunction.types index c1e16ddb33d..98bd1682455 100644 --- a/tests/baselines/reference/typeFromParamTagForFunction.types +++ b/tests/baselines/reference/typeFromParamTagForFunction.types @@ -10,9 +10,9 @@ declare var module: any, exports: any; === tests/cases/conformance/salsa/a-ext.js === exports.A = function () { >exports.A = function () { this.x = 1;} : () => void ->exports.A : any ->exports : any ->A : any +>exports.A : () => void +>exports : typeof "tests/cases/conformance/salsa/a-ext" +>A : () => void >function () { this.x = 1;} : () => void this.x = 1; @@ -42,9 +42,9 @@ function a(p) { p.x; } === tests/cases/conformance/salsa/b-ext.js === exports.B = class { >exports.B = class { constructor() { this.x = 1; }} : typeof (Anonymous class) ->exports.B : any ->exports : any ->B : any +>exports.B : typeof (Anonymous class) +>exports : typeof "tests/cases/conformance/salsa/b-ext" +>B : typeof (Anonymous class) >class { constructor() { this.x = 1; }} : typeof (Anonymous class) constructor() { diff --git a/tests/baselines/reference/untypedModuleImport_allowJs.symbols b/tests/baselines/reference/untypedModuleImport_allowJs.symbols index d660e811630..1076590e009 100644 --- a/tests/baselines/reference/untypedModuleImport_allowJs.symbols +++ b/tests/baselines/reference/untypedModuleImport_allowJs.symbols @@ -11,6 +11,7 @@ foo.bar(); // Same as untypedModuleImport.ts but with --allowJs, so the package will actually be typed. exports.default = { bar() { return 0; } } +>exports.default : Symbol(default, Decl(index.js, 0, 0)) >exports : Symbol(default, Decl(index.js, 0, 0)) >default : Symbol(default, Decl(index.js, 0, 0)) >bar : Symbol(bar, Decl(index.js, 2, 19)) diff --git a/tests/baselines/reference/untypedModuleImport_allowJs.types b/tests/baselines/reference/untypedModuleImport_allowJs.types index 108ba60ccb3..2a854ce9d23 100644 --- a/tests/baselines/reference/untypedModuleImport_allowJs.types +++ b/tests/baselines/reference/untypedModuleImport_allowJs.types @@ -13,9 +13,9 @@ foo.bar(); exports.default = { bar() { return 0; } } >exports.default = { bar() { return 0; } } : { [x: string]: any; bar(): number; } ->exports.default : any ->exports : any ->default : any +>exports.default : { [x: string]: any; bar(): number; } +>exports : typeof "/node_modules/foo/index" +>default : { [x: string]: any; bar(): number; } >{ bar() { return 0; } } : { [x: string]: any; bar(): number; } >bar : () => number >0 : 0 diff --git a/tests/cases/compiler/commonjsAccessExports.ts b/tests/cases/compiler/commonjsAccessExports.ts new file mode 100644 index 00000000000..961be3f5a23 --- /dev/null +++ b/tests/cases/compiler/commonjsAccessExports.ts @@ -0,0 +1,18 @@ +// @module: commonjs +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /a.js +exports.x = 0; +exports.x; + +// Works nested +{ + // 'exports' does not provide a contextual type to a function-class + exports.Cls = function() { + this.x = 0; + } +} + +const instance = new exports.Cls();