Allow exports assignments (#23319)

1. Allow assignment to `exports`.
2. The type of the rhs is not checked against the type of `exports`
since they are aliased declarations.

To support more complex patterns like `exports = c.name = c`, we may
have to treat `c.name` as a declaration. That will be more complicated
than this PR.
This commit is contained in:
Nathan Shively-Sanders
2018-04-11 06:49:58 -07:00
committed by GitHub
parent 01b22ff37e
commit 9ceb113ec5
9 changed files with 153 additions and 42 deletions

View File

@@ -13882,7 +13882,8 @@ namespace ts {
const assignmentKind = getAssignmentTargetKind(node);
if (assignmentKind) {
if (!(localOrExportSymbol.flags & SymbolFlags.Variable)) {
if (!(localOrExportSymbol.flags & SymbolFlags.Variable) &&
!(isInJavaScriptFile(node) && localOrExportSymbol.flags & SymbolFlags.ValueModule)) {
error(node, Diagnostics.Cannot_assign_to_0_because_it_is_not_a_variable, symbolToString(symbol));
return unknownType;
}
@@ -19858,8 +19859,9 @@ namespace ts {
// VarExpr = ValueExpr
// requires VarExpr to be classified as a reference
// A compound assignment furthermore requires VarExpr to be classified as a reference (section 4.1)
// and the type of the non - compound operation to be assignable to the type of VarExpr.
if (checkReferenceExpression(left, Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access)) {
// and the type of the non-compound operation to be assignable to the type of VarExpr.
if (checkReferenceExpression(left, Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access)
&& (!isIdentifier(left) || unescapeLeadingUnderscores(left.escapedText) !== "exports")) {
// to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported
checkTypeAssignableTo(valueType, leftType, left, /*headMessage*/ undefined);
}

View File

@@ -1,7 +1,5 @@
tests/cases/conformance/salsa/first.js(1,1): error TS2539: Cannot assign to '"tests/cases/conformance/salsa/first"' because it is not a variable.
tests/cases/conformance/salsa/first.js(1,11): error TS2304: Cannot find name 'require'.
tests/cases/conformance/salsa/first.js(2,9): error TS2339: Property 'formatters' does not exist on type 'typeof import("tests/cases/conformance/salsa/first")'.
tests/cases/conformance/salsa/second.js(1,1): error TS2539: Cannot assign to '"tests/cases/conformance/salsa/second"' because it is not a variable.
tests/cases/conformance/salsa/second.js(1,11): error TS2304: Cannot find name 'require'.
tests/cases/conformance/salsa/second.js(2,9): error TS2339: Property 'formatters' does not exist on type 'typeof import("tests/cases/conformance/salsa/second")'.
@@ -9,10 +7,8 @@ tests/cases/conformance/salsa/second.js(2,9): error TS2339: Property 'formatters
==== tests/cases/conformance/salsa/mod.js (0 errors) ====
// Based on a pattern from adonis
exports.formatters = {}
==== tests/cases/conformance/salsa/first.js (3 errors) ====
==== tests/cases/conformance/salsa/first.js (2 errors) ====
exports = require('./mod')
~~~~~~~
!!! error TS2539: Cannot assign to '"tests/cases/conformance/salsa/first"' because it is not a variable.
~~~~~~~
!!! error TS2304: Cannot find name 'require'.
exports.formatters.j = function (v) {
@@ -20,10 +16,8 @@ tests/cases/conformance/salsa/second.js(2,9): error TS2339: Property 'formatters
!!! error TS2339: Property 'formatters' does not exist on type 'typeof import("tests/cases/conformance/salsa/first")'.
return v
}
==== tests/cases/conformance/salsa/second.js (3 errors) ====
==== tests/cases/conformance/salsa/second.js (2 errors) ====
exports = require('./mod')
~~~~~~~
!!! error TS2539: Cannot assign to '"tests/cases/conformance/salsa/second"' because it is not a variable.
~~~~~~~
!!! error TS2304: Cannot find name 'require'.
exports.formatters.o = function (v) {

View File

@@ -10,7 +10,7 @@ exports.formatters = {}
=== tests/cases/conformance/salsa/first.js ===
exports = require('./mod')
>exports = require('./mod') : typeof import("tests/cases/conformance/salsa/mod")
>exports : any
>exports : typeof import("tests/cases/conformance/salsa/first")
>require('./mod') : typeof import("tests/cases/conformance/salsa/mod")
>require : any
>'./mod' : "./mod"
@@ -31,7 +31,7 @@ exports.formatters.j = function (v) {
=== tests/cases/conformance/salsa/second.js ===
exports = require('./mod')
>exports = require('./mod') : typeof import("tests/cases/conformance/salsa/mod")
>exports : any
>exports : typeof import("tests/cases/conformance/salsa/second")
>require('./mod') : typeof import("tests/cases/conformance/salsa/mod")
>require : any
>'./mod' : "./mod"

View File

@@ -147,7 +147,7 @@ module.exports.func4 = function () { };
var multipleDeclarationAlias1 = exports = module.exports;
>multipleDeclarationAlias1 : any
>exports = module.exports : any
>exports : any
>exports : typeof import("tests/cases/conformance/salsa/b")
>module.exports : any
>module : any
>exports : any
@@ -212,7 +212,7 @@ var multipleDeclarationAlias5 = module.exports = exports = {};
>module : any
>exports : any
>exports = {} : {}
>exports : any
>exports : typeof import("tests/cases/conformance/salsa/b")
>{} : {}
multipleDeclarationAlias5.func9 = function () { };
@@ -225,7 +225,7 @@ multipleDeclarationAlias5.func9 = function () { };
var multipleDeclarationAlias6 = exports = module.exports = {};
>multipleDeclarationAlias6 : { [x: string]: any; }
>exports = module.exports = {} : { [x: string]: any; }
>exports : any
>exports : typeof import("tests/cases/conformance/salsa/b")
>module.exports = {} : { [x: string]: any; }
>module.exports : any
>module : any
@@ -241,7 +241,7 @@ multipleDeclarationAlias6.func10 = function () { };
exports = module.exports = someOtherVariable = {};
>exports = module.exports = someOtherVariable = {} : {}
>exports : any
>exports : typeof import("tests/cases/conformance/salsa/b")
>module.exports = someOtherVariable = {} : {}
>module.exports : any
>module : any
@@ -268,7 +268,7 @@ module.exports.func12 = function () { };
exports = module.exports = someOtherVariable = {};
>exports = module.exports = someOtherVariable = {} : {}
>exports : any
>exports : typeof import("tests/cases/conformance/salsa/b")
>module.exports = someOtherVariable = {} : {}
>module.exports : any
>module : any
@@ -295,7 +295,7 @@ module.exports.func12 = function () { };
exports = module.exports = {};
>exports = module.exports = {} : { [x: string]: any; }
>exports : any
>exports : typeof import("tests/cases/conformance/salsa/b")
>module.exports = {} : { [x: string]: any; }
>module.exports : any
>module : any
@@ -320,7 +320,7 @@ module.exports.func14 = function () { };
exports = module.exports = {};
>exports = module.exports = {} : { [x: string]: any; }
>exports : any
>exports : typeof import("tests/cases/conformance/salsa/b")
>module.exports = {} : { [x: string]: any; }
>module.exports : any
>module : any
@@ -349,7 +349,7 @@ module.exports = exports = {};
>module : any
>exports : any
>exports = {} : {}
>exports : any
>exports : typeof import("tests/cases/conformance/salsa/b")
>{} : {}
exports.func17 = function () { };

View File

@@ -0,0 +1,51 @@
=== tests/cases/conformance/salsa/index.js ===
/// <reference path='node.d.ts' />
const C = require("./semver")
>C : Symbol(C, Decl(index.js, 1, 5))
>require : Symbol(require, Decl(node.d.ts, 0, 0))
>"./semver" : Symbol("tests/cases/conformance/salsa/semver", Decl(semver.js, 0, 0))
var two = C.f(1)
>two : Symbol(two, Decl(index.js, 2, 3))
>C.f : Symbol(f, Decl(semver.js, 1, 28))
>C : Symbol(C, Decl(index.js, 1, 5))
>f : Symbol(f, Decl(semver.js, 1, 28))
var c = new C
>c : Symbol(c, Decl(index.js, 3, 3))
>C : Symbol(C, Decl(index.js, 1, 5))
=== tests/cases/conformance/salsa/node.d.ts ===
declare function require(name: string): any;
>require : Symbol(require, Decl(node.d.ts, 0, 0))
>name : Symbol(name, Decl(node.d.ts, 0, 25))
declare var exports: any;
>exports : Symbol(exports, Decl(node.d.ts, 1, 11))
declare var module: { exports: any };
>module : Symbol(module, Decl(node.d.ts, 2, 11))
>exports : Symbol(exports, Decl(node.d.ts, 2, 21))
=== tests/cases/conformance/salsa/semver.js ===
/// <reference path='node.d.ts' />
exports = module.exports = C
>exports : Symbol("tests/cases/conformance/salsa/semver", Decl(semver.js, 0, 0))
>module.exports : Symbol(exports, Decl(node.d.ts, 2, 21))
>module : Symbol(export=, Decl(semver.js, 1, 9))
>exports : Symbol(export=, Decl(semver.js, 1, 9))
>C : Symbol(C, Decl(semver.js, 2, 22))
exports.f = n => n + 1
>exports.f : Symbol(f, Decl(semver.js, 1, 28))
>exports : Symbol(f, Decl(semver.js, 1, 28))
>f : Symbol(f, Decl(semver.js, 1, 28))
>n : Symbol(n, Decl(semver.js, 2, 11))
>n : Symbol(n, Decl(semver.js, 2, 11))
function C() {
>C : Symbol(C, Decl(semver.js, 2, 22))
this.p = 1
>p : Symbol(C.p, Decl(semver.js, 3, 14))
}

View File

@@ -0,0 +1,65 @@
=== tests/cases/conformance/salsa/index.js ===
/// <reference path='node.d.ts' />
const C = require("./semver")
>C : typeof C
>require("./semver") : typeof C
>require : (name: string) => any
>"./semver" : "./semver"
var two = C.f(1)
>two : any
>C.f(1) : any
>C.f : (n: any) => any
>C : typeof C
>f : (n: any) => any
>1 : 1
var c = new C
>c : C
>new C : C
>C : typeof C
=== tests/cases/conformance/salsa/node.d.ts ===
declare function require(name: string): any;
>require : (name: string) => any
>name : string
declare var exports: any;
>exports : any
declare var module: { exports: any };
>module : { exports: any; }
>exports : any
=== tests/cases/conformance/salsa/semver.js ===
/// <reference path='node.d.ts' />
exports = module.exports = C
>exports = module.exports = C : typeof C
>exports : typeof import("tests/cases/conformance/salsa/semver")
>module.exports = C : typeof C
>module.exports : any
>module : { exports: any; }
>exports : any
>C : typeof C
exports.f = n => n + 1
>exports.f = n => n + 1 : (n: any) => any
>exports.f : (n: any) => any
>exports : typeof import("tests/cases/conformance/salsa/semver")
>f : (n: any) => any
>n => n + 1 : (n: any) => any
>n : any
>n + 1 : any
>n : any
>1 : 1
function C() {
>C : typeof C
this.p = 1
>this.p = 1 : 1
>this.p : any
>this : any
>p : any
>1 : 1
}

View File

@@ -1,20 +0,0 @@
tests/cases/conformance/salsa/semver.js(2,1): error TS2539: Cannot assign to '"tests/cases/conformance/salsa/semver"' because it is not a variable.
==== tests/cases/conformance/salsa/index.js (0 errors) ====
/// <reference path='./types.d.ts'/>
const C = require("./semver")
var two = C.f(1)
==== tests/cases/conformance/salsa/types.d.ts (0 errors) ====
declare var require: any;
declare var module: any;
==== tests/cases/conformance/salsa/semver.js (1 errors) ====
/// <reference path='./types.d.ts'/>
exports = module.exports = C
~~~~~~~
!!! error TS2539: Cannot assign to '"tests/cases/conformance/salsa/semver"' because it is not a variable.
C.f = n => n + 1
function C() {
this.p = 1
}

View File

@@ -25,7 +25,7 @@ declare var module: any;
/// <reference path='./types.d.ts'/>
exports = module.exports = C
>exports = module.exports = C : typeof C
>exports : any
>exports : typeof import("tests/cases/conformance/salsa/semver")
>module.exports = C : typeof C
>module.exports : any
>module : any

View File

@@ -0,0 +1,19 @@
// @checkJs: true
// @allowJS: true
// @noEmit: true
// @Filename: node.d.ts
declare function require(name: string): any;
declare var exports: any;
declare var module: { exports: any };
// @Filename: semver.js
/// <reference path='node.d.ts' />
exports = module.exports = C
exports.f = n => n + 1
function C() {
this.p = 1
}
// @filename: index.js
/// <reference path='node.d.ts' />
const C = require("./semver")
var two = C.f(1)
var c = new C