Skip to content

Commit 0d84f21

Browse files
authored
In JS, assignment to void 0 isn't a declaration (microsoft#39452)
Previously, property assignments with `void 0` initialisers were treated like any other values. But this causes us to choke when checking our own commonjs emit. This is something that happens by mistake a fair amount, so this PR goes back to treating these assignments as normal assignments. This should allow us to check our own emit in loose cases without harming other code bases, since `void 0` is rarely written by hand. Note that other errors still happen: noImplicitAny forbids accessing undeclared properties on object literals, and strictNullChecks forbids assigning `undefined` to properties with a different type. However, this change is enough to unblock compilation with `strictNullChecks: false`.
1 parent 17d8b02 commit 0d84f21

10 files changed

+326
-1
lines changed

src/compiler/utilities.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2220,7 +2220,7 @@ namespace ts {
22202220
}
22212221
return AssignmentDeclarationKind.ObjectDefinePropertyValue;
22222222
}
2223-
if (expr.operatorToken.kind !== SyntaxKind.EqualsToken || !isAccessExpression(expr.left)) {
2223+
if (expr.operatorToken.kind !== SyntaxKind.EqualsToken || !isAccessExpression(expr.left) || isVoidZero(getRightMostAssignedExpression(expr))) {
22242224
return AssignmentDeclarationKind.None;
22252225
}
22262226
if (isBindableStaticNameExpression(expr.left.expression, /*excludeThisKeyword*/ true) && getElementOrPropertyAccessName(expr.left) === "prototype" && isObjectLiteralExpression(getInitializerOfBinaryExpression(expr))) {
@@ -2230,6 +2230,10 @@ namespace ts {
22302230
return getAssignmentDeclarationPropertyAccessKind(expr.left);
22312231
}
22322232

2233+
function isVoidZero(node: Node) {
2234+
return isVoidExpression(node) && isNumericLiteral(node.expression) && node.expression.text === "0";
2235+
}
2236+
22332237
/**
22342238
* Does not handle signed numeric names like `a[+0]` - handling those would require handling prefix unary expressions
22352239
* throughout late binding handling as well, which is awkward (but ultimately probably doable if there is demand)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [assignmentToVoidZero1.js]
2+
// #38552
3+
exports.y = exports.x = void 0;
4+
exports.x = 1;
5+
exports.y = 2;
6+
7+
8+
//// [assignmentToVoidZero1.js]
9+
// #38552
10+
exports.y = exports.x = void 0;
11+
exports.x = 1;
12+
exports.y = 2;
13+
14+
15+
//// [assignmentToVoidZero1.d.ts]
16+
export var x: number;
17+
export var y: number;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
=== tests/cases/conformance/salsa/assignmentToVoidZero1.js ===
2+
// #38552
3+
exports.y = exports.x = void 0;
4+
>exports.y : Symbol(y, Decl(assignmentToVoidZero1.js, 2, 14))
5+
>exports : Symbol("tests/cases/conformance/salsa/assignmentToVoidZero1", Decl(assignmentToVoidZero1.js, 0, 0))
6+
>y : Symbol(y, Decl(assignmentToVoidZero1.js, 2, 14))
7+
>exports.x : Symbol(x, Decl(assignmentToVoidZero1.js, 1, 31))
8+
>exports : Symbol("tests/cases/conformance/salsa/assignmentToVoidZero1", Decl(assignmentToVoidZero1.js, 0, 0))
9+
>x : Symbol(x, Decl(assignmentToVoidZero1.js, 1, 31))
10+
11+
exports.x = 1;
12+
>exports.x : Symbol(x, Decl(assignmentToVoidZero1.js, 1, 31))
13+
>exports : Symbol(x, Decl(assignmentToVoidZero1.js, 1, 31))
14+
>x : Symbol(x, Decl(assignmentToVoidZero1.js, 1, 31))
15+
16+
exports.y = 2;
17+
>exports.y : Symbol(y, Decl(assignmentToVoidZero1.js, 2, 14))
18+
>exports : Symbol(y, Decl(assignmentToVoidZero1.js, 2, 14))
19+
>y : Symbol(y, Decl(assignmentToVoidZero1.js, 2, 14))
20+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
=== tests/cases/conformance/salsa/assignmentToVoidZero1.js ===
2+
// #38552
3+
exports.y = exports.x = void 0;
4+
>exports.y = exports.x = void 0 : undefined
5+
>exports.y : number
6+
>exports : typeof import("tests/cases/conformance/salsa/assignmentToVoidZero1")
7+
>y : number
8+
>exports.x = void 0 : undefined
9+
>exports.x : number
10+
>exports : typeof import("tests/cases/conformance/salsa/assignmentToVoidZero1")
11+
>x : number
12+
>void 0 : undefined
13+
>0 : 0
14+
15+
exports.x = 1;
16+
>exports.x = 1 : 1
17+
>exports.x : number
18+
>exports : typeof import("tests/cases/conformance/salsa/assignmentToVoidZero1")
19+
>x : number
20+
>1 : 1
21+
22+
exports.y = 2;
23+
>exports.y = 2 : 2
24+
>exports.y : number
25+
>exports : typeof import("tests/cases/conformance/salsa/assignmentToVoidZero1")
26+
>y : number
27+
>2 : 2
28+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
tests/cases/conformance/salsa/assignmentToVoidZero2.js(2,9): error TS2339: Property 'k' does not exist on type 'typeof import("tests/cases/conformance/salsa/assignmentToVoidZero2")'.
2+
tests/cases/conformance/salsa/assignmentToVoidZero2.js(5,3): error TS2339: Property 'y' does not exist on type 'typeof o'.
3+
tests/cases/conformance/salsa/assignmentToVoidZero2.js(6,9): error TS2339: Property 'y' does not exist on type 'typeof o'.
4+
tests/cases/conformance/salsa/assignmentToVoidZero2.js(13,9): error TS2339: Property 'q' does not exist on type 'C'.
5+
tests/cases/conformance/salsa/importer.js(1,13): error TS2305: Module '"./assignmentToVoidZero2"' has no exported member 'k'.
6+
7+
8+
==== tests/cases/conformance/salsa/assignmentToVoidZero2.js (4 errors) ====
9+
exports.j = 1;
10+
exports.k = void 0;
11+
~
12+
!!! error TS2339: Property 'k' does not exist on type 'typeof import("tests/cases/conformance/salsa/assignmentToVoidZero2")'.
13+
var o = {}
14+
o.x = 1
15+
o.y = void 0
16+
~
17+
!!! error TS2339: Property 'y' does not exist on type 'typeof o'.
18+
o.x + o.y
19+
~
20+
!!! error TS2339: Property 'y' does not exist on type 'typeof o'.
21+
22+
function C() {
23+
this.p = 1
24+
this.q = void 0
25+
}
26+
var c = new C()
27+
c.p + c.q
28+
~
29+
!!! error TS2339: Property 'q' does not exist on type 'C'.
30+
31+
==== tests/cases/conformance/salsa/importer.js (1 errors) ====
32+
import { j, k } from './assignmentToVoidZero2'
33+
~
34+
!!! error TS2305: Module '"./assignmentToVoidZero2"' has no exported member 'k'.
35+
j + k
36+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//// [tests/cases/conformance/salsa/assignmentToVoidZero2.ts] ////
2+
3+
//// [assignmentToVoidZero2.js]
4+
exports.j = 1;
5+
exports.k = void 0;
6+
var o = {}
7+
o.x = 1
8+
o.y = void 0
9+
o.x + o.y
10+
11+
function C() {
12+
this.p = 1
13+
this.q = void 0
14+
}
15+
var c = new C()
16+
c.p + c.q
17+
18+
//// [importer.js]
19+
import { j, k } from './assignmentToVoidZero2'
20+
j + k
21+
22+
23+
//// [assignmentToVoidZero2.js]
24+
exports.j = 1;
25+
exports.k = void 0;
26+
var o = {};
27+
o.x = 1;
28+
o.y = void 0;
29+
o.x + o.y;
30+
function C() {
31+
this.p = 1;
32+
this.q = void 0;
33+
}
34+
var c = new C();
35+
c.p + c.q;
36+
//// [importer.js]
37+
"use strict";
38+
exports.__esModule = true;
39+
var assignmentToVoidZero2_1 = require("./assignmentToVoidZero2");
40+
assignmentToVoidZero2_1.j + assignmentToVoidZero2_1.k;
41+
42+
43+
//// [assignmentToVoidZero2.d.ts]
44+
export var j: number;
45+
//// [importer.d.ts]
46+
export {};
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
=== tests/cases/conformance/salsa/assignmentToVoidZero2.js ===
2+
exports.j = 1;
3+
>exports.j : Symbol(j, Decl(assignmentToVoidZero2.js, 0, 0))
4+
>exports : Symbol(j, Decl(assignmentToVoidZero2.js, 0, 0))
5+
>j : Symbol(j, Decl(assignmentToVoidZero2.js, 0, 0))
6+
7+
exports.k = void 0;
8+
>exports : Symbol("tests/cases/conformance/salsa/assignmentToVoidZero2", Decl(assignmentToVoidZero2.js, 0, 0))
9+
10+
var o = {}
11+
>o : Symbol(o, Decl(assignmentToVoidZero2.js, 2, 3), Decl(assignmentToVoidZero2.js, 2, 10))
12+
13+
o.x = 1
14+
>o.x : Symbol(o.x, Decl(assignmentToVoidZero2.js, 2, 10))
15+
>o : Symbol(o, Decl(assignmentToVoidZero2.js, 2, 3), Decl(assignmentToVoidZero2.js, 2, 10))
16+
>x : Symbol(o.x, Decl(assignmentToVoidZero2.js, 2, 10))
17+
18+
o.y = void 0
19+
>o : Symbol(o, Decl(assignmentToVoidZero2.js, 2, 3), Decl(assignmentToVoidZero2.js, 2, 10))
20+
21+
o.x + o.y
22+
>o.x : Symbol(o.x, Decl(assignmentToVoidZero2.js, 2, 10))
23+
>o : Symbol(o, Decl(assignmentToVoidZero2.js, 2, 3), Decl(assignmentToVoidZero2.js, 2, 10))
24+
>x : Symbol(o.x, Decl(assignmentToVoidZero2.js, 2, 10))
25+
>o : Symbol(o, Decl(assignmentToVoidZero2.js, 2, 3), Decl(assignmentToVoidZero2.js, 2, 10))
26+
27+
function C() {
28+
>C : Symbol(C, Decl(assignmentToVoidZero2.js, 5, 9))
29+
30+
this.p = 1
31+
>p : Symbol(C.p, Decl(assignmentToVoidZero2.js, 7, 14))
32+
33+
this.q = void 0
34+
}
35+
var c = new C()
36+
>c : Symbol(c, Decl(assignmentToVoidZero2.js, 11, 3))
37+
>C : Symbol(C, Decl(assignmentToVoidZero2.js, 5, 9))
38+
39+
c.p + c.q
40+
>c.p : Symbol(C.p, Decl(assignmentToVoidZero2.js, 7, 14))
41+
>c : Symbol(c, Decl(assignmentToVoidZero2.js, 11, 3))
42+
>p : Symbol(C.p, Decl(assignmentToVoidZero2.js, 7, 14))
43+
>c : Symbol(c, Decl(assignmentToVoidZero2.js, 11, 3))
44+
45+
=== tests/cases/conformance/salsa/importer.js ===
46+
import { j, k } from './assignmentToVoidZero2'
47+
>j : Symbol(j, Decl(importer.js, 0, 8))
48+
>k : Symbol(k, Decl(importer.js, 0, 11))
49+
50+
j + k
51+
>j : Symbol(j, Decl(importer.js, 0, 8))
52+
>k : Symbol(k, Decl(importer.js, 0, 11))
53+
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
=== tests/cases/conformance/salsa/assignmentToVoidZero2.js ===
2+
exports.j = 1;
3+
>exports.j = 1 : 1
4+
>exports.j : number
5+
>exports : typeof import("tests/cases/conformance/salsa/assignmentToVoidZero2")
6+
>j : number
7+
>1 : 1
8+
9+
exports.k = void 0;
10+
>exports.k = void 0 : undefined
11+
>exports.k : any
12+
>exports : typeof import("tests/cases/conformance/salsa/assignmentToVoidZero2")
13+
>k : any
14+
>void 0 : undefined
15+
>0 : 0
16+
17+
var o = {}
18+
>o : typeof o
19+
>{} : {}
20+
21+
o.x = 1
22+
>o.x = 1 : 1
23+
>o.x : number
24+
>o : typeof o
25+
>x : number
26+
>1 : 1
27+
28+
o.y = void 0
29+
>o.y = void 0 : undefined
30+
>o.y : any
31+
>o : typeof o
32+
>y : any
33+
>void 0 : undefined
34+
>0 : 0
35+
36+
o.x + o.y
37+
>o.x + o.y : any
38+
>o.x : number
39+
>o : typeof o
40+
>x : number
41+
>o.y : any
42+
>o : typeof o
43+
>y : any
44+
45+
function C() {
46+
>C : typeof C
47+
48+
this.p = 1
49+
>this.p = 1 : 1
50+
>this.p : any
51+
>this : any
52+
>p : any
53+
>1 : 1
54+
55+
this.q = void 0
56+
>this.q = void 0 : undefined
57+
>this.q : any
58+
>this : any
59+
>q : any
60+
>void 0 : undefined
61+
>0 : 0
62+
}
63+
var c = new C()
64+
>c : C
65+
>new C() : C
66+
>C : typeof C
67+
68+
c.p + c.q
69+
>c.p + c.q : any
70+
>c.p : number
71+
>c : C
72+
>p : number
73+
>c.q : any
74+
>c : C
75+
>q : any
76+
77+
=== tests/cases/conformance/salsa/importer.js ===
78+
import { j, k } from './assignmentToVoidZero2'
79+
>j : number
80+
>k : any
81+
82+
j + k
83+
>j + k : any
84+
>j : number
85+
>k : any
86+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// @filename: assignmentToVoidZero1.js
2+
// @declaration: true
3+
// @module: commonjs
4+
// @outdir: auss
5+
// @checkJs: true
6+
// @allowJs: true
7+
8+
// #38552
9+
exports.y = exports.x = void 0;
10+
exports.x = 1;
11+
exports.y = 2;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// @filename: assignmentToVoidZero2.js
2+
// @declaration: true
3+
// @module: commonjs
4+
// @outdir: auss
5+
// @checkJs: true
6+
// @allowJs: true
7+
// @noImplicitAny: true
8+
exports.j = 1;
9+
exports.k = void 0;
10+
var o = {}
11+
o.x = 1
12+
o.y = void 0
13+
o.x + o.y
14+
15+
function C() {
16+
this.p = 1
17+
this.q = void 0
18+
}
19+
var c = new C()
20+
c.p + c.q
21+
22+
// @filename: importer.js
23+
import { j, k } from './assignmentToVoidZero2'
24+
j + k

0 commit comments

Comments
 (0)