Skip to content

Commit 485a2ea

Browse files
authored
Error on class fields accesses through super in JS files (#55892)
1 parent 9cf44dc commit 485a2ea

16 files changed

+638
-1
lines changed

src/compiler/utilitiesPublic.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ import {
102102
isArrowFunction,
103103
isAssignmentExpression,
104104
isBinaryExpression,
105+
isBindableStaticAccessExpression,
105106
isBindableStaticElementAccessExpression,
107+
isBindableStaticNameExpression,
106108
isBindingElement,
107109
isBlock,
108110
isCallExpression,
@@ -111,6 +113,7 @@ import {
111113
isClassStaticBlockDeclaration,
112114
isDecorator,
113115
isElementAccessExpression,
116+
isExpandoPropertyDeclaration,
114117
isExportAssignment,
115118
isExportDeclaration,
116119
isExportSpecifier,
@@ -153,6 +156,7 @@ import {
153156
isPropertyAccessExpression,
154157
isPropertyAssignment,
155158
isPropertyDeclaration,
159+
isPrototypeAccess,
156160
isRootedDiskPath,
157161
isSourceFile,
158162
isStringLiteral,
@@ -1705,7 +1709,10 @@ export function isAutoAccessorPropertyDeclaration(node: Node): node is AutoAcces
17051709
}
17061710

17071711
/** @internal */
1708-
export function isClassFieldAndNotAutoAccessor(node: Node): boolean {
1712+
export function isClassFieldAndNotAutoAccessor(node: Declaration): boolean {
1713+
if (isInJSFile(node) && isExpandoPropertyDeclaration(node)) {
1714+
return (!isBindableStaticAccessExpression(node) || !isPrototypeAccess(node.expression)) && !isBindableStaticNameExpression(node, /*excludeThisKeyword*/ true);
1715+
}
17091716
return node.parent && isClassLike(node.parent) && isPropertyDeclaration(node) && !hasAccessorModifier(node);
17101717
}
17111718

tests/baselines/reference/classFieldSuperAccessible.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ class C extends Array {
1616
console.log(super.length);
1717
}
1818
}
19+
20+
class D {
21+
accessor b = () => {}
22+
}
23+
class E extends D {
24+
foo() {
25+
super.b()
26+
}
27+
}
1928

2029

2130
//// [classFieldSuperAccessible.js]
@@ -35,3 +44,11 @@ class C extends Array {
3544
console.log(super.length);
3645
}
3746
}
47+
class D {
48+
accessor b = () => { };
49+
}
50+
class E extends D {
51+
foo() {
52+
super.b();
53+
}
54+
}

tests/baselines/reference/classFieldSuperAccessible.symbols

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,23 @@ class C extends Array {
4646
}
4747
}
4848

49+
class D {
50+
>D : Symbol(D, Decl(classFieldSuperAccessible.ts, 14, 1))
51+
52+
accessor b = () => {}
53+
>b : Symbol(D.b, Decl(classFieldSuperAccessible.ts, 16, 9))
54+
}
55+
class E extends D {
56+
>E : Symbol(E, Decl(classFieldSuperAccessible.ts, 18, 1))
57+
>D : Symbol(D, Decl(classFieldSuperAccessible.ts, 14, 1))
58+
59+
foo() {
60+
>foo : Symbol(E.foo, Decl(classFieldSuperAccessible.ts, 19, 19))
61+
62+
super.b()
63+
>super.b : Symbol(D.b, Decl(classFieldSuperAccessible.ts, 16, 9))
64+
>super : Symbol(D, Decl(classFieldSuperAccessible.ts, 14, 1))
65+
>b : Symbol(D.b, Decl(classFieldSuperAccessible.ts, 16, 9))
66+
}
67+
}
68+

tests/baselines/reference/classFieldSuperAccessible.types

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,25 @@ class C extends Array {
5050
}
5151
}
5252

53+
class D {
54+
>D : D
55+
56+
accessor b = () => {}
57+
>b : () => void
58+
>() => {} : () => void
59+
}
60+
class E extends D {
61+
>E : E
62+
>D : D
63+
64+
foo() {
65+
>foo : () => void
66+
67+
super.b()
68+
>super.b() : void
69+
>super.b : () => void
70+
>super : D
71+
>b : () => void
72+
}
73+
}
74+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
index.js(9,23): error TS2565: Property 'blah2' is used before being assigned.
2+
3+
4+
==== index.js (1 errors) ====
5+
class C {
6+
static blah1 = 123;
7+
}
8+
C.blah2 = 456;
9+
10+
class D extends C {
11+
static {
12+
console.log(super.blah1);
13+
console.log(super.blah2);
14+
~~~~~
15+
!!! error TS2565: Property 'blah2' is used before being assigned.
16+
}
17+
}
18+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//// [tests/cases/compiler/classFieldSuperAccessibleJs1.ts] ////
2+
3+
=== index.js ===
4+
class C {
5+
>C : Symbol(C, Decl(index.js, 0, 0), Decl(index.js, 2, 1))
6+
7+
static blah1 = 123;
8+
>blah1 : Symbol(C.blah1, Decl(index.js, 0, 9))
9+
}
10+
C.blah2 = 456;
11+
>C.blah2 : Symbol(C.blah2, Decl(index.js, 2, 1))
12+
>C : Symbol(C, Decl(index.js, 0, 0), Decl(index.js, 2, 1))
13+
>blah2 : Symbol(C.blah2, Decl(index.js, 2, 1))
14+
15+
class D extends C {
16+
>D : Symbol(D, Decl(index.js, 3, 14))
17+
>C : Symbol(C, Decl(index.js, 0, 0), Decl(index.js, 2, 1))
18+
19+
static {
20+
console.log(super.blah1);
21+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
22+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
23+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
24+
>super.blah1 : Symbol(C.blah1, Decl(index.js, 0, 9))
25+
>super : Symbol(C, Decl(index.js, 0, 0), Decl(index.js, 2, 1))
26+
>blah1 : Symbol(C.blah1, Decl(index.js, 0, 9))
27+
28+
console.log(super.blah2);
29+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
30+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
31+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
32+
>super.blah2 : Symbol(C.blah2, Decl(index.js, 2, 1))
33+
>super : Symbol(C, Decl(index.js, 0, 0), Decl(index.js, 2, 1))
34+
>blah2 : Symbol(C.blah2, Decl(index.js, 2, 1))
35+
}
36+
}
37+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//// [tests/cases/compiler/classFieldSuperAccessibleJs1.ts] ////
2+
3+
=== index.js ===
4+
class C {
5+
>C : C
6+
7+
static blah1 = 123;
8+
>blah1 : number
9+
>123 : 123
10+
}
11+
C.blah2 = 456;
12+
>C.blah2 = 456 : 456
13+
>C.blah2 : number
14+
>C : typeof C
15+
>blah2 : number
16+
>456 : 456
17+
18+
class D extends C {
19+
>D : D
20+
>C : C
21+
22+
static {
23+
console.log(super.blah1);
24+
>console.log(super.blah1) : void
25+
>console.log : (...data: any[]) => void
26+
>console : Console
27+
>log : (...data: any[]) => void
28+
>super.blah1 : number
29+
>super : typeof C
30+
>blah1 : number
31+
32+
console.log(super.blah2);
33+
>console.log(super.blah2) : void
34+
>console.log : (...data: any[]) => void
35+
>console : Console
36+
>log : (...data: any[]) => void
37+
>super.blah2 : number
38+
>super : typeof C
39+
>blah2 : number
40+
}
41+
}
42+
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//// [tests/cases/compiler/classFieldSuperAccessibleJs2.ts] ////
2+
3+
=== index.js ===
4+
class C {
5+
>C : Symbol(C, Decl(index.js, 0, 0))
6+
7+
constructor() {
8+
this.foo = () => {
9+
>this.foo : Symbol(C.foo, Decl(index.js, 5, 3))
10+
>this : Symbol(C, Decl(index.js, 0, 0))
11+
>foo : Symbol(C.foo, Decl(index.js, 1, 17))
12+
13+
console.log("called arrow");
14+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
15+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
16+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
17+
18+
};
19+
}
20+
foo() {
21+
>foo : Symbol(C.foo, Decl(index.js, 5, 3))
22+
23+
console.log("called method");
24+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
25+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
26+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
27+
}
28+
}
29+
30+
class D extends C {
31+
>D : Symbol(D, Decl(index.js, 9, 1))
32+
>C : Symbol(C, Decl(index.js, 0, 0))
33+
34+
foo() {
35+
>foo : Symbol(D.foo, Decl(index.js, 11, 19))
36+
37+
console.log("SUPER:");
38+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
39+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
40+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
41+
42+
super.foo();
43+
>super.foo : Symbol(C.foo, Decl(index.js, 5, 3))
44+
>super : Symbol(C, Decl(index.js, 0, 0))
45+
>foo : Symbol(C.foo, Decl(index.js, 5, 3))
46+
47+
console.log("THIS:");
48+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
49+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
50+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
51+
52+
this.foo();
53+
>this.foo : Symbol(D.foo, Decl(index.js, 11, 19))
54+
>this : Symbol(D, Decl(index.js, 9, 1))
55+
>foo : Symbol(D.foo, Decl(index.js, 11, 19))
56+
}
57+
}
58+
59+
const obj = new D();
60+
>obj : Symbol(obj, Decl(index.js, 20, 5))
61+
>D : Symbol(D, Decl(index.js, 9, 1))
62+
63+
obj.foo();
64+
>obj.foo : Symbol(D.foo, Decl(index.js, 11, 19))
65+
>obj : Symbol(obj, Decl(index.js, 20, 5))
66+
>foo : Symbol(D.foo, Decl(index.js, 11, 19))
67+
68+
D.prototype.foo.call(obj);
69+
>D.prototype.foo.call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --))
70+
>D.prototype.foo : Symbol(D.foo, Decl(index.js, 11, 19))
71+
>D.prototype : Symbol(D.prototype)
72+
>D : Symbol(D, Decl(index.js, 9, 1))
73+
>prototype : Symbol(D.prototype)
74+
>foo : Symbol(D.foo, Decl(index.js, 11, 19))
75+
>call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --))
76+
>obj : Symbol(obj, Decl(index.js, 20, 5))
77+
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//// [tests/cases/compiler/classFieldSuperAccessibleJs2.ts] ////
2+
3+
=== index.js ===
4+
class C {
5+
>C : C
6+
7+
constructor() {
8+
this.foo = () => {
9+
>this.foo = () => { console.log("called arrow"); } : () => void
10+
>this.foo : () => void
11+
>this : this
12+
>foo : () => void
13+
>() => { console.log("called arrow"); } : () => void
14+
15+
console.log("called arrow");
16+
>console.log("called arrow") : void
17+
>console.log : (...data: any[]) => void
18+
>console : Console
19+
>log : (...data: any[]) => void
20+
>"called arrow" : "called arrow"
21+
22+
};
23+
}
24+
foo() {
25+
>foo : () => void
26+
27+
console.log("called method");
28+
>console.log("called method") : void
29+
>console.log : (...data: any[]) => void
30+
>console : Console
31+
>log : (...data: any[]) => void
32+
>"called method" : "called method"
33+
}
34+
}
35+
36+
class D extends C {
37+
>D : D
38+
>C : C
39+
40+
foo() {
41+
>foo : () => void
42+
43+
console.log("SUPER:");
44+
>console.log("SUPER:") : void
45+
>console.log : (...data: any[]) => void
46+
>console : Console
47+
>log : (...data: any[]) => void
48+
>"SUPER:" : "SUPER:"
49+
50+
super.foo();
51+
>super.foo() : void
52+
>super.foo : () => void
53+
>super : C
54+
>foo : () => void
55+
56+
console.log("THIS:");
57+
>console.log("THIS:") : void
58+
>console.log : (...data: any[]) => void
59+
>console : Console
60+
>log : (...data: any[]) => void
61+
>"THIS:" : "THIS:"
62+
63+
this.foo();
64+
>this.foo() : void
65+
>this.foo : () => void
66+
>this : this
67+
>foo : () => void
68+
}
69+
}
70+
71+
const obj = new D();
72+
>obj : D
73+
>new D() : D
74+
>D : typeof D
75+
76+
obj.foo();
77+
>obj.foo() : void
78+
>obj.foo : () => void
79+
>obj : D
80+
>foo : () => void
81+
82+
D.prototype.foo.call(obj);
83+
>D.prototype.foo.call(obj) : void
84+
>D.prototype.foo.call : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
85+
>D.prototype.foo : () => void
86+
>D.prototype : D
87+
>D : typeof D
88+
>prototype : D
89+
>foo : () => void
90+
>call : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
91+
>obj : D
92+

0 commit comments

Comments
 (0)