Allow to access exports from inside a commonjs module (#17745)

* Allow to access `exports` from inside a commonjs module

* Don't contextually type `this` in `exports.f = function() { ... }`

* Update test
This commit is contained in:
Andy
2017-09-14 12:36:55 -07:00
committed by GitHub
parent 3062c6309b
commit d1e2242ee4
14 changed files with 194 additions and 57 deletions
+22 -4
View File
@@ -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 && (<BinaryExpression>func.parent).operatorToken.kind === SyntaxKind.EqualsToken) {
const target = (<BinaryExpression>func.parent).left;
const { parent } = func;
if (parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>parent).operatorToken.kind === SyntaxKind.EqualsToken) {
const target = (<BinaryExpression>parent).left;
if (target.kind === SyntaxKind.PropertyAccessExpression || target.kind === SyntaxKind.ElementAccessExpression) {
return checkExpressionCached((<PropertyAccessExpression | ElementAccessExpression>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);
}
}
}
@@ -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))
@@ -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
@@ -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))
@@ -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;
@@ -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))
@@ -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 () { };
@@ -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))
@@ -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
@@ -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))
@@ -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() {
@@ -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))
@@ -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
@@ -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();