Skip to content

Commit 1edc975

Browse files
authored
Revert the revert of explicitly typed special assignments (#25727)
* Revert "Revert "Explicitly typed special assignments are context sensitive (#25619)"" This reverts commit 16676f2. * Revert "Revert "Explicitly typed prototype assignments are context sensitive (#25688)"" This reverts commit ff8c30d.
1 parent d9ed917 commit 1edc975

13 files changed

+617
-59
lines changed

src/compiler/checker.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4714,6 +4714,10 @@ namespace ts {
47144714
// function/class/{} assignments are fresh declarations, not property assignments, so only add prototype assignments
47154715
const specialDeclaration = getAssignedJavascriptInitializer(symbol.valueDeclaration);
47164716
if (specialDeclaration) {
4717+
const tag = getJSDocTypeTag(specialDeclaration);
4718+
if (tag && tag.typeExpression) {
4719+
return getTypeFromTypeNode(tag.typeExpression);
4720+
}
47174721
return getWidenedLiteralType(checkExpressionCached(specialDeclaration));
47184722
}
47194723
const types: Type[] = [];
@@ -5081,7 +5085,7 @@ namespace ts {
50815085
}
50825086

50835087
function getJSInitializerType(decl: Node, symbol: Symbol, init: Expression | undefined): Type | undefined {
5084-
if (init && isInJavaScriptFile(init) && isObjectLiteralExpression(init)) {
5088+
if (init && isInJavaScriptFile(init) && isObjectLiteralExpression(init) && init.properties.length === 0) {
50855089
const exports = createSymbolTable();
50865090
while (isBinaryExpression(decl) || isPropertyAccessExpression(decl)) {
50875091
const s = getSymbolOfNode(decl);
@@ -15786,22 +15790,22 @@ namespace ts {
1578615790
}
1578715791

1578815792
// In an assignment expression, the right operand is contextually typed by the type of the left operand.
15789-
// Don't do this for special property assignments to avoid circularity.
15793+
// Don't do this for special property assignments unless there is a type tag on the assignment, to avoid circularity from checking the right operand.
1579015794
function isContextSensitiveAssignment(binaryExpression: BinaryExpression): boolean {
1579115795
const kind = getSpecialPropertyAssignmentKind(binaryExpression);
1579215796
switch (kind) {
1579315797
case SpecialPropertyAssignmentKind.None:
1579415798
return true;
1579515799
case SpecialPropertyAssignmentKind.Property:
15796-
// If `binaryExpression.left` was assigned a symbol, then this is a new declaration; otherwise it is an assignment to an existing declaration.
15797-
// See `bindStaticPropertyAssignment` in `binder.ts`.
15798-
return !binaryExpression.left.symbol;
1579915800
case SpecialPropertyAssignmentKind.ExportsProperty:
15800-
case SpecialPropertyAssignmentKind.ModuleExports:
15801+
case SpecialPropertyAssignmentKind.Prototype:
1580115802
case SpecialPropertyAssignmentKind.PrototypeProperty:
15803+
// If `binaryExpression.left` was assigned a symbol, then this is a new declaration; otherwise it is an assignment to an existing declaration.
15804+
// See `bindStaticPropertyAssignment` in `binder.ts`.
15805+
return !binaryExpression.left.symbol || binaryExpression.left.symbol.valueDeclaration && !!getJSDocTypeTag(binaryExpression.left.symbol.valueDeclaration);
1580215806
case SpecialPropertyAssignmentKind.ThisProperty:
15803-
case SpecialPropertyAssignmentKind.Prototype:
15804-
return false;
15807+
case SpecialPropertyAssignmentKind.ModuleExports:
15808+
return !binaryExpression.symbol || binaryExpression.symbol.valueDeclaration && !!getJSDocTypeTag(binaryExpression.symbol.valueDeclaration);
1580515809
default:
1580615810
return Debug.assertNever(kind);
1580715811
}

tests/baselines/reference/chainedPrototypeAssignment.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ A.prototype = B.prototype = {
8686
>A : typeof A
8787
>prototype : { [x: string]: any; m(n: number): number; }
8888
>B.prototype = { /** @param {number} n */ m(n) { return n + 1 }} : { [x: string]: any; m(n: number): number; }
89-
>B.prototype : { [x: string]: any; }
89+
>B.prototype : { [x: string]: any; m(n: number): number; }
9090
>B : typeof B
91-
>prototype : { [x: string]: any; }
91+
>prototype : { [x: string]: any; m(n: number): number; }
9292
>{ /** @param {number} n */ m(n) { return n + 1 }} : { [x: string]: any; m(n: number): number; }
9393

9494
/** @param {number} n */

tests/baselines/reference/conflictingCommonJSES2015Exports.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ export function abc(a, b, c) { return 5; }
77
>5 : 5
88

99
module.exports = { abc };
10-
>module.exports = { abc } : { [x: string]: any; abc: (a: any, b: any, c: any) => number; }
10+
>module.exports = { abc } : { abc: (a: any, b: any, c: any) => number; }
1111
>module.exports : any
1212
>module : any
1313
>exports : any
14-
>{ abc } : { [x: string]: any; abc: (a: any, b: any, c: any) => number; }
14+
>{ abc } : { abc: (a: any, b: any, c: any) => number; }
1515
>abc : (a: any, b: any, c: any) => number
1616

1717
=== tests/cases/conformance/salsa/use.js ===
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
tests/cases/conformance/salsa/mod.js(5,7): error TS7006: Parameter 'n' implicitly has an 'any' type.
2+
tests/cases/conformance/salsa/test.js(52,7): error TS7006: Parameter 'n' implicitly has an 'any' type.
3+
4+
5+
==== tests/cases/conformance/salsa/test.js (1 errors) ====
6+
/** @typedef {{
7+
status: 'done'
8+
m(n: number): void
9+
}} DoneStatus */
10+
11+
// property assignment
12+
var ns = {}
13+
/** @type {DoneStatus} */
14+
ns.x = {
15+
status: 'done',
16+
m(n) { }
17+
}
18+
19+
ns.x = {
20+
status: 'done',
21+
m(n) { }
22+
}
23+
ns.x
24+
25+
26+
// this-property assignment
27+
class Thing {
28+
constructor() {
29+
/** @type {DoneStatus} */
30+
this.s = {
31+
status: 'done',
32+
m(n) { }
33+
}
34+
}
35+
36+
fail() {
37+
this.s = {
38+
status: 'done',
39+
m(n) { }
40+
}
41+
}
42+
}
43+
44+
// exports-property assignment
45+
46+
/** @type {DoneStatus} */
47+
exports.x = {
48+
status: "done",
49+
m(n) { }
50+
}
51+
exports.x
52+
53+
/** @type {DoneStatus} contextual typing is allowed, but module.exports.y: any.
54+
Guess it doesn't check the type tag? */
55+
module.exports.y = {
56+
status: "done",
57+
m(n) { }
58+
~
59+
!!! error TS7006: Parameter 'n' implicitly has an 'any' type.
60+
}
61+
module.exports.y
62+
63+
// prototype-property assignment
64+
/** @type {DoneStatus} */
65+
Thing.prototype.x = {
66+
status: 'done',
67+
m(n) { }
68+
}
69+
Thing.prototype.x
70+
71+
// prototype assignment
72+
function F() {
73+
}
74+
/** @type {DoneStatus} */
75+
F.prototype = {
76+
status: "done",
77+
m(n) { }
78+
}
79+
80+
==== tests/cases/conformance/salsa/mod.js (1 errors) ====
81+
// module.exports assignment
82+
/** @type {{ status: 'done', m(n: number): void }} */
83+
module.exports = {
84+
status: "done",
85+
m(n) { }
86+
~
87+
!!! error TS7006: Parameter 'n' implicitly has an 'any' type.
88+
}
89+
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
=== tests/cases/conformance/salsa/test.js ===
2+
/** @typedef {{
3+
status: 'done'
4+
m(n: number): void
5+
}} DoneStatus */
6+
7+
// property assignment
8+
var ns = {}
9+
>ns : Symbol(ns, Decl(test.js, 6, 3))
10+
11+
/** @type {DoneStatus} */
12+
ns.x = {
13+
>ns.x : Symbol(ns.x, Decl(test.js, 6, 11), Decl(test.js, 11, 1))
14+
>ns : Symbol(ns, Decl(test.js, 6, 3))
15+
>x : Symbol(ns.x, Decl(test.js, 6, 11), Decl(test.js, 11, 1))
16+
17+
status: 'done',
18+
>status : Symbol(status, Decl(test.js, 8, 8))
19+
20+
m(n) { }
21+
>m : Symbol(m, Decl(test.js, 9, 19))
22+
>n : Symbol(n, Decl(test.js, 10, 6))
23+
}
24+
25+
ns.x = {
26+
>ns.x : Symbol(ns.x, Decl(test.js, 6, 11), Decl(test.js, 11, 1))
27+
>ns : Symbol(ns, Decl(test.js, 6, 3))
28+
>x : Symbol(ns.x, Decl(test.js, 6, 11), Decl(test.js, 11, 1))
29+
30+
status: 'done',
31+
>status : Symbol(status, Decl(test.js, 13, 8))
32+
33+
m(n) { }
34+
>m : Symbol(m, Decl(test.js, 14, 19))
35+
>n : Symbol(n, Decl(test.js, 15, 6))
36+
}
37+
ns.x
38+
>ns.x : Symbol(ns.x, Decl(test.js, 6, 11), Decl(test.js, 11, 1))
39+
>ns : Symbol(ns, Decl(test.js, 6, 3))
40+
>x : Symbol(ns.x, Decl(test.js, 6, 11), Decl(test.js, 11, 1))
41+
42+
43+
// this-property assignment
44+
class Thing {
45+
>Thing : Symbol(Thing, Decl(test.js, 17, 4))
46+
47+
constructor() {
48+
/** @type {DoneStatus} */
49+
this.s = {
50+
>this.s : Symbol(Thing.s, Decl(test.js, 22, 19), Decl(test.js, 30, 12))
51+
>this : Symbol(Thing, Decl(test.js, 17, 4))
52+
>s : Symbol(Thing.s, Decl(test.js, 22, 19), Decl(test.js, 30, 12))
53+
54+
status: 'done',
55+
>status : Symbol(status, Decl(test.js, 24, 18))
56+
57+
m(n) { }
58+
>m : Symbol(m, Decl(test.js, 25, 27))
59+
>n : Symbol(n, Decl(test.js, 26, 14))
60+
}
61+
}
62+
63+
fail() {
64+
>fail : Symbol(Thing.fail, Decl(test.js, 28, 5))
65+
66+
this.s = {
67+
>this.s : Symbol(Thing.s, Decl(test.js, 22, 19), Decl(test.js, 30, 12))
68+
>this : Symbol(Thing, Decl(test.js, 17, 4))
69+
>s : Symbol(Thing.s, Decl(test.js, 22, 19), Decl(test.js, 30, 12))
70+
71+
status: 'done',
72+
>status : Symbol(status, Decl(test.js, 31, 18))
73+
74+
m(n) { }
75+
>m : Symbol(m, Decl(test.js, 32, 27))
76+
>n : Symbol(n, Decl(test.js, 33, 14))
77+
}
78+
}
79+
}
80+
81+
// exports-property assignment
82+
83+
/** @type {DoneStatus} */
84+
exports.x = {
85+
>exports.x : Symbol(x, Decl(test.js, 36, 1))
86+
>exports : Symbol(x, Decl(test.js, 36, 1))
87+
>x : Symbol(x, Decl(test.js, 36, 1))
88+
89+
status: "done",
90+
>status : Symbol(status, Decl(test.js, 41, 13))
91+
92+
m(n) { }
93+
>m : Symbol(m, Decl(test.js, 42, 19))
94+
>n : Symbol(n, Decl(test.js, 43, 6))
95+
}
96+
exports.x
97+
>exports.x : Symbol(x, Decl(test.js, 36, 1))
98+
>exports : Symbol("tests/cases/conformance/salsa/test", Decl(test.js, 0, 0))
99+
>x : Symbol(x, Decl(test.js, 36, 1))
100+
101+
/** @type {DoneStatus} contextual typing is allowed, but module.exports.y: any.
102+
Guess it doesn't check the type tag? */
103+
module.exports.y = {
104+
>module.exports : Symbol(y, Decl(test.js, 45, 9))
105+
>module : Symbol(module)
106+
>y : Symbol(y, Decl(test.js, 45, 9))
107+
108+
status: "done",
109+
>status : Symbol(status, Decl(test.js, 49, 20))
110+
111+
m(n) { }
112+
>m : Symbol(m, Decl(test.js, 50, 19))
113+
>n : Symbol(n, Decl(test.js, 51, 6))
114+
}
115+
module.exports.y
116+
>module : Symbol(module)
117+
118+
// prototype-property assignment
119+
/** @type {DoneStatus} */
120+
Thing.prototype.x = {
121+
>Thing.prototype.x : Symbol(Thing.x, Decl(test.js, 53, 16))
122+
>Thing.prototype : Symbol(Thing.x, Decl(test.js, 53, 16))
123+
>Thing : Symbol(Thing, Decl(test.js, 17, 4))
124+
>prototype : Symbol(Thing.prototype)
125+
>x : Symbol(Thing.x, Decl(test.js, 53, 16))
126+
127+
status: 'done',
128+
>status : Symbol(status, Decl(test.js, 57, 21))
129+
130+
m(n) { }
131+
>m : Symbol(m, Decl(test.js, 58, 19))
132+
>n : Symbol(n, Decl(test.js, 59, 6))
133+
}
134+
Thing.prototype.x
135+
>Thing.prototype.x : Symbol(Thing.x, Decl(test.js, 53, 16))
136+
>Thing.prototype : Symbol(Thing.prototype)
137+
>Thing : Symbol(Thing, Decl(test.js, 17, 4))
138+
>prototype : Symbol(Thing.prototype)
139+
>x : Symbol(Thing.x, Decl(test.js, 53, 16))
140+
141+
// prototype assignment
142+
function F() {
143+
>F : Symbol(F, Decl(test.js, 61, 17), Decl(test.js, 65, 1))
144+
}
145+
/** @type {DoneStatus} */
146+
F.prototype = {
147+
>F.prototype : Symbol(F.prototype, Decl(test.js, 65, 1))
148+
>F : Symbol(F, Decl(test.js, 61, 17), Decl(test.js, 65, 1))
149+
>prototype : Symbol(F.prototype, Decl(test.js, 65, 1))
150+
151+
status: "done",
152+
>status : Symbol(status, Decl(test.js, 67, 15))
153+
154+
m(n) { }
155+
>m : Symbol(m, Decl(test.js, 68, 19))
156+
>n : Symbol(n, Decl(test.js, 69, 6))
157+
}
158+
159+
=== tests/cases/conformance/salsa/mod.js ===
160+
// module.exports assignment
161+
/** @type {{ status: 'done', m(n: number): void }} */
162+
module.exports = {
163+
>module : Symbol(export=, Decl(mod.js, 0, 0))
164+
>exports : Symbol(export=, Decl(mod.js, 0, 0))
165+
166+
status: "done",
167+
>status : Symbol(status, Decl(mod.js, 2, 18))
168+
169+
m(n) { }
170+
>m : Symbol(m, Decl(mod.js, 3, 19))
171+
>n : Symbol(n, Decl(mod.js, 4, 6))
172+
}
173+

0 commit comments

Comments
 (0)