Skip to content

Commit 50ef631

Browse files
authored
Support prototype assignment with a function declaration (#25300)
Previously variable declaration+function expression worked. Note that class expression/class declaration do not work, due to the way they are specified. I added a test for future reference.
1 parent 1579bfd commit 50ef631

8 files changed

+179
-1
lines changed

src/compiler/checker.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19458,7 +19458,8 @@ namespace ts {
1945819458
function getAssignedClassType(symbol: Symbol) {
1945919459
const decl = symbol.valueDeclaration;
1946019460
const assignmentSymbol = decl && decl.parent &&
19461-
(isBinaryExpression(decl.parent) && getSymbolOfNode(decl.parent.left) ||
19461+
(isFunctionDeclaration(decl) && getSymbolOfNode(decl) ||
19462+
isBinaryExpression(decl.parent) && getSymbolOfNode(decl.parent.left) ||
1946219463
isVariableDeclaration(decl.parent) && getSymbolOfNode(decl.parent));
1946319464
if (assignmentSymbol) {
1946419465
const prototype = forEach(assignmentSymbol.declarations, getAssignedJavascriptPrototype);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
=== tests/cases/conformance/salsa/a.js ===
2+
// mixed prototype-assignment+function declaration
3+
function C() { this.p = 1; }
4+
>C : Symbol(C, Decl(a.js, 0, 0), Decl(a.js, 1, 28))
5+
>p : Symbol(C.p, Decl(a.js, 1, 14))
6+
7+
C.prototype = { q: 2 };
8+
>C.prototype : Symbol(C.prototype, Decl(a.js, 1, 28))
9+
>C : Symbol(C, Decl(a.js, 0, 0), Decl(a.js, 1, 28))
10+
>prototype : Symbol(C.prototype, Decl(a.js, 1, 28))
11+
>q : Symbol(q, Decl(a.js, 2, 15))
12+
13+
const c = new C()
14+
>c : Symbol(c, Decl(a.js, 4, 5))
15+
>C : Symbol(C, Decl(a.js, 0, 0), Decl(a.js, 1, 28))
16+
17+
c.p
18+
>c.p : Symbol(C.p, Decl(a.js, 1, 14))
19+
>c : Symbol(c, Decl(a.js, 4, 5))
20+
>p : Symbol(C.p, Decl(a.js, 1, 14))
21+
22+
c.q
23+
>c.q : Symbol(q, Decl(a.js, 2, 15))
24+
>c : Symbol(c, Decl(a.js, 4, 5))
25+
>q : Symbol(q, Decl(a.js, 2, 15))
26+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
=== tests/cases/conformance/salsa/a.js ===
2+
// mixed prototype-assignment+function declaration
3+
function C() { this.p = 1; }
4+
>C : typeof C
5+
>this.p = 1 : 1
6+
>this.p : any
7+
>this : any
8+
>p : any
9+
>1 : 1
10+
11+
C.prototype = { q: 2 };
12+
>C.prototype = { q: 2 } : { [x: string]: any; q: number; }
13+
>C.prototype : { [x: string]: any; }
14+
>C : typeof C
15+
>prototype : { [x: string]: any; }
16+
>{ q: 2 } : { [x: string]: any; q: number; }
17+
>q : number
18+
>2 : 2
19+
20+
const c = new C()
21+
>c : C & { [x: string]: any; q: number; }
22+
>new C() : C & { [x: string]: any; q: number; }
23+
>C : typeof C
24+
25+
c.p
26+
>c.p : number
27+
>c : C & { [x: string]: any; q: number; }
28+
>p : number
29+
30+
c.q
31+
>c.q : number
32+
>c : C & { [x: string]: any; q: number; }
33+
>q : number
34+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
tests/cases/conformance/salsa/a.js(7,17): error TS2322: Type '{ [x: string]: any; q: number; }' is not assignable to type 'C'.
2+
Object literal may only specify known properties, and 'q' does not exist in type 'C'.
3+
tests/cases/conformance/salsa/a.js(11,3): error TS2339: Property 'q' does not exist on type 'C'.
4+
5+
6+
==== tests/cases/conformance/salsa/a.js (2 errors) ====
7+
// mixed prototype-assignment+class declaration
8+
class C { constructor() { this.p = 1; } }
9+
// Property assignment does nothing.
10+
// You have to use Object.defineProperty(C, "prototype", { q: 2 })
11+
// and that only works on classes with no superclass.
12+
// (Object.defineProperty isn't recognised as a JS special assignment right now.)
13+
C.prototype = { q: 2 };
14+
~~~~
15+
!!! error TS2322: Type '{ [x: string]: any; q: number; }' is not assignable to type 'C'.
16+
!!! error TS2322: Object literal may only specify known properties, and 'q' does not exist in type 'C'.
17+
18+
const c = new C()
19+
c.p
20+
c.q
21+
~
22+
!!! error TS2339: Property 'q' does not exist on type 'C'.
23+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
=== tests/cases/conformance/salsa/a.js ===
2+
// mixed prototype-assignment+class declaration
3+
class C { constructor() { this.p = 1; } }
4+
>C : Symbol(C, Decl(a.js, 0, 0), Decl(a.js, 1, 41))
5+
>this.p : Symbol(C.p, Decl(a.js, 1, 25))
6+
>this : Symbol(C, Decl(a.js, 0, 0), Decl(a.js, 1, 41))
7+
>p : Symbol(C.p, Decl(a.js, 1, 25))
8+
9+
// Property assignment does nothing.
10+
// You have to use Object.defineProperty(C, "prototype", { q: 2 })
11+
// and that only works on classes with no superclass.
12+
// (Object.defineProperty isn't recognised as a JS special assignment right now.)
13+
C.prototype = { q: 2 };
14+
>C.prototype : Symbol(C.prototype, Decl(a.js, 1, 41))
15+
>C : Symbol(C, Decl(a.js, 0, 0), Decl(a.js, 1, 41))
16+
>prototype : Symbol(C.prototype, Decl(a.js, 1, 41))
17+
>q : Symbol(q, Decl(a.js, 6, 15))
18+
19+
const c = new C()
20+
>c : Symbol(c, Decl(a.js, 8, 5))
21+
>C : Symbol(C, Decl(a.js, 0, 0), Decl(a.js, 1, 41))
22+
23+
c.p
24+
>c.p : Symbol(C.p, Decl(a.js, 1, 25))
25+
>c : Symbol(c, Decl(a.js, 8, 5))
26+
>p : Symbol(C.p, Decl(a.js, 1, 25))
27+
28+
c.q
29+
>c : Symbol(c, Decl(a.js, 8, 5))
30+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
=== tests/cases/conformance/salsa/a.js ===
2+
// mixed prototype-assignment+class declaration
3+
class C { constructor() { this.p = 1; } }
4+
>C : C
5+
>this.p = 1 : 1
6+
>this.p : number
7+
>this : this
8+
>p : number
9+
>1 : 1
10+
11+
// Property assignment does nothing.
12+
// You have to use Object.defineProperty(C, "prototype", { q: 2 })
13+
// and that only works on classes with no superclass.
14+
// (Object.defineProperty isn't recognised as a JS special assignment right now.)
15+
C.prototype = { q: 2 };
16+
>C.prototype = { q: 2 } : { [x: string]: any; q: number; }
17+
>C.prototype : C
18+
>C : typeof C
19+
>prototype : C
20+
>{ q: 2 } : { [x: string]: any; q: number; }
21+
>q : number
22+
>2 : 2
23+
24+
const c = new C()
25+
>c : C
26+
>new C() : C
27+
>C : typeof C
28+
29+
c.p
30+
>c.p : number
31+
>c : C
32+
>p : number
33+
34+
c.q
35+
>c.q : any
36+
>c : C
37+
>q : any
38+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// @noEmit: true
2+
// @allowJs: true
3+
// @checkJs: true
4+
// @Filename: a.js
5+
// mixed prototype-assignment+function declaration
6+
function C() { this.p = 1; }
7+
C.prototype = { q: 2 };
8+
9+
const c = new C()
10+
c.p
11+
c.q
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// @noEmit: true
2+
// @allowJs: true
3+
// @checkJs: true
4+
// @Filename: a.js
5+
// mixed prototype-assignment+class declaration
6+
class C { constructor() { this.p = 1; } }
7+
// Property assignment does nothing.
8+
// You have to use Object.defineProperty(C, "prototype", { q: 2 })
9+
// and that only works on classes with no superclass.
10+
// (Object.defineProperty isn't recognised as a JS special assignment right now.)
11+
C.prototype = { q: 2 };
12+
13+
const c = new C()
14+
c.p
15+
c.q

0 commit comments

Comments
 (0)