Skip to content

Commit 5b21cbc

Browse files
authored
Explicitly typed prototype assignments are context sensitive (#25688)
* Explicitly typed prototype assignments:ctx sensitive Follow up to #25619: Add the necessary code to type `prototype` correctly in prototype assignments so that code like `F.prototype = { ... }` properly makes the object literal context sensitive. * Fix lint
1 parent 60986ad commit 5b21cbc

11 files changed

+53
-52
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 1 deletion
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);

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/contextualTypedSpecialAssignment.errors.txt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
tests/cases/conformance/salsa/mod.js(5,7): error TS7006: Parameter 'n' implicitly has an 'any' type.
22
tests/cases/conformance/salsa/test.js(52,7): error TS7006: Parameter 'n' implicitly has an 'any' type.
3-
tests/cases/conformance/salsa/test.js(70,7): error TS7006: Parameter 'n' implicitly has an 'any' type.
43

54

6-
==== tests/cases/conformance/salsa/test.js (2 errors) ====
5+
==== tests/cases/conformance/salsa/test.js (1 errors) ====
76
/** @typedef {{
87
status: 'done'
98
m(n: number): void
@@ -76,13 +75,11 @@ tests/cases/conformance/salsa/test.js(70,7): error TS7006: Parameter 'n' implici
7675
F.prototype = {
7776
status: "done",
7877
m(n) { }
79-
~
80-
!!! error TS7006: Parameter 'n' implicitly has an 'any' type.
8178
}
8279

8380
==== tests/cases/conformance/salsa/mod.js (1 errors) ====
8481
// module.exports assignment
85-
/** @type {{ status: 'done' }} */
82+
/** @type {{ status: 'done', m(n: number): void }} */
8683
module.exports = {
8784
status: "done",
8885
m(n) { }

tests/baselines/reference/contextualTypedSpecialAssignment.symbols

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ F.prototype = {
158158

159159
=== tests/cases/conformance/salsa/mod.js ===
160160
// module.exports assignment
161-
/** @type {{ status: 'done' }} */
161+
/** @type {{ status: 'done', m(n: number): void }} */
162162
module.exports = {
163163
>module : Symbol(export=, Decl(mod.js, 0, 0))
164164
>exports : Symbol(export=, Decl(mod.js, 0, 0))

tests/baselines/reference/contextualTypedSpecialAssignment.types

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -172,24 +172,24 @@ function F() {
172172
}
173173
/** @type {DoneStatus} */
174174
F.prototype = {
175-
>F.prototype = { status: "done", m(n) { }} : { status: string; m(n: any): void; }
176-
>F.prototype : { [x: string]: any; }
175+
>F.prototype = { status: "done", m(n) { }} : { status: "done"; m(n: number): void; }
176+
>F.prototype : { status: "done"; m(n: number): void; }
177177
>F : typeof F
178-
>prototype : { [x: string]: any; }
179-
>{ status: "done", m(n) { }} : { status: string; m(n: any): void; }
178+
>prototype : { status: "done"; m(n: number): void; }
179+
>{ status: "done", m(n) { }} : { status: "done"; m(n: number): void; }
180180

181181
status: "done",
182-
>status : string
182+
>status : "done"
183183
>"done" : "done"
184184

185185
m(n) { }
186-
>m : (n: any) => void
187-
>n : any
186+
>m : (n: number) => void
187+
>n : number
188188
}
189189

190190
=== tests/cases/conformance/salsa/mod.js ===
191191
// module.exports assignment
192-
/** @type {{ status: 'done' }} */
192+
/** @type {{ status: 'done', m(n: number): void }} */
193193
module.exports = {
194194
>module.exports = { status: "done", m(n) { }} : { status: string; m(n: any): void; }
195195
>module.exports : any

tests/baselines/reference/typeFromPropertyAssignment11.types

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ var Inner = function() {}
55

66
Inner.prototype = {
77
>Inner.prototype = { m() { }, i: 1} : { [x: string]: any; m(): void; i: number; }
8-
>Inner.prototype : { [x: string]: any; }
8+
>Inner.prototype : { [x: string]: any; m(): void; i: number; }
99
>Inner : typeof Inner
10-
>prototype : { [x: string]: any; }
10+
>prototype : { [x: string]: any; m(): void; i: number; }
1111
>{ m() { }, i: 1} : { [x: string]: any; m(): void; i: number; }
1212

1313
m() { },
@@ -21,18 +21,18 @@ Inner.prototype = {
2121
Inner.prototype.j = 2
2222
>Inner.prototype.j = 2 : 2
2323
>Inner.prototype.j : any
24-
>Inner.prototype : { [x: string]: any; }
24+
>Inner.prototype : { [x: string]: any; m(): void; i: number; }
2525
>Inner : typeof Inner
26-
>prototype : { [x: string]: any; }
26+
>prototype : { [x: string]: any; m(): void; i: number; }
2727
>j : any
2828
>2 : 2
2929

3030
/** @type {string} */
3131
Inner.prototype.k;
3232
>Inner.prototype.k : any
33-
>Inner.prototype : { [x: string]: any; }
33+
>Inner.prototype : { [x: string]: any; m(): void; i: number; }
3434
>Inner : typeof Inner
35-
>prototype : { [x: string]: any; }
35+
>prototype : { [x: string]: any; m(): void; i: number; }
3636
>k : any
3737

3838
var inner = new Inner()

tests/baselines/reference/typeFromPropertyAssignment13.types

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ Outer.Inner = function() {}
1212

1313
Outer.Inner.prototype = {
1414
>Outer.Inner.prototype = { m() { }, i: 1} : { [x: string]: any; m(): void; i: number; }
15-
>Outer.Inner.prototype : { [x: string]: any; }
15+
>Outer.Inner.prototype : { [x: string]: any; m(): void; i: number; }
1616
>Outer.Inner : typeof Inner
1717
>Outer : typeof Outer
1818
>Inner : typeof Inner
19-
>prototype : { [x: string]: any; }
19+
>prototype : { [x: string]: any; m(): void; i: number; }
2020
>{ m() { }, i: 1} : { [x: string]: any; m(): void; i: number; }
2121

2222
m() { },
@@ -30,22 +30,22 @@ Outer.Inner.prototype = {
3030
Outer.Inner.prototype.j = 2
3131
>Outer.Inner.prototype.j = 2 : 2
3232
>Outer.Inner.prototype.j : any
33-
>Outer.Inner.prototype : { [x: string]: any; }
33+
>Outer.Inner.prototype : { [x: string]: any; m(): void; i: number; }
3434
>Outer.Inner : typeof Inner
3535
>Outer : typeof Outer
3636
>Inner : typeof Inner
37-
>prototype : { [x: string]: any; }
37+
>prototype : { [x: string]: any; m(): void; i: number; }
3838
>j : any
3939
>2 : 2
4040

4141
/** @type {string} */
4242
Outer.Inner.prototype.k;
4343
>Outer.Inner.prototype.k : any
44-
>Outer.Inner.prototype : { [x: string]: any; }
44+
>Outer.Inner.prototype : { [x: string]: any; m(): void; i: number; }
4545
>Outer.Inner : typeof Inner
4646
>Outer : typeof Outer
4747
>Inner : typeof Inner
48-
>prototype : { [x: string]: any; }
48+
>prototype : { [x: string]: any; m(): void; i: number; }
4949
>k : any
5050

5151
var inner = new Outer.Inner()

tests/baselines/reference/typeFromPropertyAssignment14.types

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@ var Outer = {};
55

66
=== tests/cases/conformance/salsa/work.js ===
77
Outer.Inner = function () {}
8-
>Outer.Inner = function () {} : { (): void; prototype: { [x: string]: any; }; }
9-
>Outer.Inner : { (): void; prototype: { [x: string]: any; }; }
8+
>Outer.Inner = function () {} : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
9+
>Outer.Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
1010
>Outer : typeof Outer
11-
>Inner : { (): void; prototype: { [x: string]: any; }; }
12-
>function () {} : { (): void; prototype: { [x: string]: any; }; }
11+
>Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
12+
>function () {} : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
1313

1414
Outer.Inner.prototype = {
1515
>Outer.Inner.prototype = { x: 1, m() { }} : { [x: string]: any; x: number; m(): void; }
16-
>Outer.Inner.prototype : { [x: string]: any; }
17-
>Outer.Inner : { (): void; prototype: { [x: string]: any; }; }
16+
>Outer.Inner.prototype : { [x: string]: any; x: number; m(): void; }
17+
>Outer.Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
1818
>Outer : typeof Outer
19-
>Inner : { (): void; prototype: { [x: string]: any; }; }
20-
>prototype : { [x: string]: any; }
19+
>Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
20+
>prototype : { [x: string]: any; x: number; m(): void; }
2121
>{ x: 1, m() { }} : { [x: string]: any; x: number; m(): void; }
2222

2323
x: 1,
@@ -47,9 +47,9 @@ inner.m()
4747
var inno = new Outer.Inner()
4848
>inno : { [x: string]: any; x: number; m(): void; }
4949
>new Outer.Inner() : { [x: string]: any; x: number; m(): void; }
50-
>Outer.Inner : { (): void; prototype: { [x: string]: any; }; }
50+
>Outer.Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
5151
>Outer : typeof Outer
52-
>Inner : { (): void; prototype: { [x: string]: any; }; }
52+
>Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
5353

5454
inno.x
5555
>inno.x : number

tests/baselines/reference/typeFromPropertyAssignment16.types

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@ var Outer = {};
44
>{} : { [x: string]: any; }
55

66
Outer.Inner = function () {}
7-
>Outer.Inner = function () {} : { (): void; prototype: { [x: string]: any; }; }
8-
>Outer.Inner : { (): void; prototype: { [x: string]: any; }; }
7+
>Outer.Inner = function () {} : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
8+
>Outer.Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
99
>Outer : typeof Outer
10-
>Inner : { (): void; prototype: { [x: string]: any; }; }
11-
>function () {} : { (): void; prototype: { [x: string]: any; }; }
10+
>Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
11+
>function () {} : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
1212

1313
Outer.Inner.prototype = {
1414
>Outer.Inner.prototype = { x: 1, m() { }} : { [x: string]: any; x: number; m(): void; }
15-
>Outer.Inner.prototype : { [x: string]: any; }
16-
>Outer.Inner : { (): void; prototype: { [x: string]: any; }; }
15+
>Outer.Inner.prototype : { [x: string]: any; x: number; m(): void; }
16+
>Outer.Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
1717
>Outer : typeof Outer
18-
>Inner : { (): void; prototype: { [x: string]: any; }; }
19-
>prototype : { [x: string]: any; }
18+
>Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
19+
>prototype : { [x: string]: any; x: number; m(): void; }
2020
>{ x: 1, m() { }} : { [x: string]: any; x: number; m(): void; }
2121

2222
x: 1,
@@ -45,9 +45,9 @@ inner.m()
4545
var inno = new Outer.Inner()
4646
>inno : { [x: string]: any; x: number; m(): void; }
4747
>new Outer.Inner() : { [x: string]: any; x: number; m(): void; }
48-
>Outer.Inner : { (): void; prototype: { [x: string]: any; }; }
48+
>Outer.Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
4949
>Outer : typeof Outer
50-
>Inner : { (): void; prototype: { [x: string]: any; }; }
50+
>Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; }
5151

5252
inno.x
5353
>inno.x : number

tests/baselines/reference/typeFromPropertyAssignment27.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ function C() { this.p = 1; }
1010

1111
C.prototype = { q: 2 };
1212
>C.prototype = { q: 2 } : { [x: string]: any; q: number; }
13-
>C.prototype : { [x: string]: any; }
13+
>C.prototype : { [x: string]: any; q: number; }
1414
>C : typeof C
15-
>prototype : { [x: string]: any; }
15+
>prototype : { [x: string]: any; q: number; }
1616
>{ q: 2 } : { [x: string]: any; q: number; }
1717
>q : number
1818
>2 : 2

tests/cases/conformance/salsa/contextualTypedSpecialAssignment.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ F.prototype = {
7777

7878
// @Filename: mod.js
7979
// module.exports assignment
80-
/** @type {{ status: 'done' }} */
80+
/** @type {{ status: 'done', m(n: number): void }} */
8181
module.exports = {
8282
status: "done",
8383
m(n) { }

0 commit comments

Comments
 (0)