Skip to content

Commit 5f818a9

Browse files
Jack-Workssandersn
andauthored
fix: class field is not accessible via super (#54056)
Co-authored-by: Nathan Shively-Sanders <[email protected]>
1 parent e743d07 commit 5f818a9

13 files changed

+300
-1
lines changed

src/compiler/checker.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ import {
477477
isClassDeclaration,
478478
isClassElement,
479479
isClassExpression,
480+
isClassFieldAndNotAutoAccessor,
480481
isClassLike,
481482
isClassStaticBlockDeclaration,
482483
isCommaSequence,
@@ -31820,6 +31821,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3182031821
}
3182131822
return false;
3182231823
}
31824+
// A class field cannot be accessed via super.* from a derived class.
31825+
// This is true for both [[Set]] (old) and [[Define]] (ES spec) semantics.
31826+
if (!(flags & ModifierFlags.Static) && prop.declarations?.some(isClassFieldAndNotAutoAccessor)) {
31827+
if (errorNode) {
31828+
error(errorNode, Diagnostics.Class_field_0_defined_by_the_parent_class_is_not_accessible_in_the_child_class_via_super, symbolToString(prop));
31829+
}
31830+
return false;
31831+
}
3182331832
}
3182431833

3182531834
// Referencing abstract properties within their own constructors is not allowed

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3663,6 +3663,10 @@
36633663
"category": "Error",
36643664
"code": 2854
36653665
},
3666+
"Class field '{0}' defined by the parent class is not accessible in the child class via super.": {
3667+
"category": "Error",
3668+
"code": 2855
3669+
},
36663670

36673671
"Import declaration '{0}' is using private name '{1}'.": {
36683672
"category": "Error",

src/compiler/utilitiesPublic.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1704,6 +1704,11 @@ export function isAutoAccessorPropertyDeclaration(node: Node): node is AutoAcces
17041704
return isPropertyDeclaration(node) && hasAccessorModifier(node);
17051705
}
17061706

1707+
/** @internal */
1708+
export function isClassFieldAndNotAutoAccessor(node: Node): boolean {
1709+
return node.parent && isClassLike(node.parent) && isPropertyDeclaration(node) && !hasAccessorModifier(node);
1710+
}
1711+
17071712
/** @internal */
17081713
export function isMethodOrAccessor(node: Node): node is MethodDeclaration | AccessorDeclaration {
17091714
switch (node.kind) {

tests/baselines/reference/checkSuperCallBeforeThisAccess.errors.txt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
checkSuperCallBeforeThisAccess.ts(7,18): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
22
checkSuperCallBeforeThisAccess.ts(8,18): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
33
checkSuperCallBeforeThisAccess.ts(9,18): error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
4+
checkSuperCallBeforeThisAccess.ts(9,24): error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super.
5+
checkSuperCallBeforeThisAccess.ts(12,30): error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super.
6+
checkSuperCallBeforeThisAccess.ts(17,28): error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super.
47
checkSuperCallBeforeThisAccess.ts(20,22): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
58
checkSuperCallBeforeThisAccess.ts(21,22): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
69
checkSuperCallBeforeThisAccess.ts(22,22): error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
10+
checkSuperCallBeforeThisAccess.ts(22,28): error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super.
711
checkSuperCallBeforeThisAccess.ts(30,30): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
812
checkSuperCallBeforeThisAccess.ts(39,22): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
913
checkSuperCallBeforeThisAccess.ts(43,18): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
1014
checkSuperCallBeforeThisAccess.ts(44,18): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
1115
checkSuperCallBeforeThisAccess.ts(45,18): error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
16+
checkSuperCallBeforeThisAccess.ts(45,24): error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super.
1217
checkSuperCallBeforeThisAccess.ts(59,27): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
1318
checkSuperCallBeforeThisAccess.ts(75,27): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
1419

1520

16-
==== checkSuperCallBeforeThisAccess.ts (13 errors) ====
21+
==== checkSuperCallBeforeThisAccess.ts (18 errors) ====
1722
class A {
1823
x = 1;
1924
}
@@ -29,14 +34,20 @@ checkSuperCallBeforeThisAccess.ts(75,27): error TS17009: 'super' must be called
2934
let a3 = super.x; // Error
3035
~~~~~
3136
!!! error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
37+
~
38+
!!! error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super.
3239
let a4 = () => this;
3340
let a5 = () => this.x;
3441
let a6 = () => super.x;
42+
~
43+
!!! error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super.
3544
if (!!true) {
3645
super();
3746
let b1 = this;
3847
let b2 = this.x;
3948
let b3 = super.x;
49+
~
50+
!!! error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super.
4051
}
4152
else {
4253
let c1 = this; // Error
@@ -48,6 +59,8 @@ checkSuperCallBeforeThisAccess.ts(75,27): error TS17009: 'super' must be called
4859
let c3 = super.x; // Error
4960
~~~~~
5061
!!! error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
62+
~
63+
!!! error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super.
5164
}
5265
if (!!true) {
5366
switch (n) {
@@ -81,6 +94,8 @@ checkSuperCallBeforeThisAccess.ts(75,27): error TS17009: 'super' must be called
8194
let f3 = super.x; // Error
8295
~~~~~
8396
!!! error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
97+
~
98+
!!! error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super.
8499
}
85100
}
86101

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//// [tests/cases/compiler/classFieldSuperAccessible.ts] ////
2+
3+
//// [classFieldSuperAccessible.ts]
4+
class A extends class Expr {} {
5+
static {
6+
console.log(super.name);
7+
}
8+
}
9+
class B extends Number {
10+
static {
11+
console.log(super.EPSILON);
12+
}
13+
}
14+
class C extends Array {
15+
foo() {
16+
console.log(super.length);
17+
}
18+
}
19+
20+
21+
//// [classFieldSuperAccessible.js]
22+
class A extends class Expr {
23+
} {
24+
static {
25+
console.log(super.name);
26+
}
27+
}
28+
class B extends Number {
29+
static {
30+
console.log(super.EPSILON);
31+
}
32+
}
33+
class C extends Array {
34+
foo() {
35+
console.log(super.length);
36+
}
37+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//// [tests/cases/compiler/classFieldSuperAccessible.ts] ////
2+
3+
=== classFieldSuperAccessible.ts ===
4+
class A extends class Expr {} {
5+
>A : Symbol(A, Decl(classFieldSuperAccessible.ts, 0, 0))
6+
>Expr : Symbol(Expr, Decl(classFieldSuperAccessible.ts, 0, 15))
7+
8+
static {
9+
console.log(super.name);
10+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
11+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
12+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
13+
>super.name : Symbol(Function.name, Decl(lib.es2015.core.d.ts, --, --))
14+
>super : Symbol(Expr, Decl(classFieldSuperAccessible.ts, 0, 15))
15+
>name : Symbol(Function.name, Decl(lib.es2015.core.d.ts, --, --))
16+
}
17+
}
18+
class B extends Number {
19+
>B : Symbol(B, Decl(classFieldSuperAccessible.ts, 4, 1))
20+
>Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2020.number.d.ts, --, --))
21+
22+
static {
23+
console.log(super.EPSILON);
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+
>super.EPSILON : Symbol(NumberConstructor.EPSILON, Decl(lib.es2015.core.d.ts, --, --))
28+
>super : Symbol(NumberConstructor, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
29+
>EPSILON : Symbol(NumberConstructor.EPSILON, Decl(lib.es2015.core.d.ts, --, --))
30+
}
31+
}
32+
class C extends Array {
33+
>C : Symbol(C, Decl(classFieldSuperAccessible.ts, 9, 1))
34+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more)
35+
36+
foo() {
37+
>foo : Symbol(C.foo, Decl(classFieldSuperAccessible.ts, 10, 23))
38+
39+
console.log(super.length);
40+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
41+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
42+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
43+
>super.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
44+
>super : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more)
45+
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
46+
}
47+
}
48+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//// [tests/cases/compiler/classFieldSuperAccessible.ts] ////
2+
3+
=== classFieldSuperAccessible.ts ===
4+
class A extends class Expr {} {
5+
>A : A
6+
>class Expr {} : Expr
7+
>Expr : typeof Expr
8+
9+
static {
10+
console.log(super.name);
11+
>console.log(super.name) : void
12+
>console.log : (...data: any[]) => void
13+
>console : Console
14+
>log : (...data: any[]) => void
15+
>super.name : string
16+
>super : typeof Expr
17+
>name : string
18+
}
19+
}
20+
class B extends Number {
21+
>B : B
22+
>Number : Number
23+
24+
static {
25+
console.log(super.EPSILON);
26+
>console.log(super.EPSILON) : void
27+
>console.log : (...data: any[]) => void
28+
>console : Console
29+
>log : (...data: any[]) => void
30+
>super.EPSILON : number
31+
>super : NumberConstructor
32+
>EPSILON : number
33+
}
34+
}
35+
class C extends Array {
36+
>C : C
37+
>Array : any[]
38+
39+
foo() {
40+
>foo : () => void
41+
42+
console.log(super.length);
43+
>console.log(super.length) : void
44+
>console.log : (...data: any[]) => void
45+
>console : Console
46+
>log : (...data: any[]) => void
47+
>super.length : number
48+
>super : any[]
49+
>length : number
50+
}
51+
}
52+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
classFieldSuperNotAccessible.ts(6,15): error TS2855: Class field 'field' defined by the parent class is not accessible in the child class via super.
2+
3+
4+
==== classFieldSuperNotAccessible.ts (1 errors) ====
5+
class T {
6+
field = () => {}
7+
}
8+
class T2 extends T {
9+
f() {
10+
super.field() // error
11+
~~~~~
12+
!!! error TS2855: Class field 'field' defined by the parent class is not accessible in the child class via super.
13+
}
14+
}
15+
16+
new T2().f()
17+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//// [tests/cases/compiler/classFieldSuperNotAccessible.ts] ////
2+
3+
//// [classFieldSuperNotAccessible.ts]
4+
class T {
5+
field = () => {}
6+
}
7+
class T2 extends T {
8+
f() {
9+
super.field() // error
10+
}
11+
}
12+
13+
new T2().f()
14+
15+
16+
//// [classFieldSuperNotAccessible.js]
17+
class T {
18+
field = () => { };
19+
}
20+
class T2 extends T {
21+
f() {
22+
super.field(); // error
23+
}
24+
}
25+
new T2().f();
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//// [tests/cases/compiler/classFieldSuperNotAccessible.ts] ////
2+
3+
=== classFieldSuperNotAccessible.ts ===
4+
class T {
5+
>T : Symbol(T, Decl(classFieldSuperNotAccessible.ts, 0, 0))
6+
7+
field = () => {}
8+
>field : Symbol(T.field, Decl(classFieldSuperNotAccessible.ts, 0, 9))
9+
}
10+
class T2 extends T {
11+
>T2 : Symbol(T2, Decl(classFieldSuperNotAccessible.ts, 2, 1))
12+
>T : Symbol(T, Decl(classFieldSuperNotAccessible.ts, 0, 0))
13+
14+
f() {
15+
>f : Symbol(T2.f, Decl(classFieldSuperNotAccessible.ts, 3, 20))
16+
17+
super.field() // error
18+
>super.field : Symbol(T.field, Decl(classFieldSuperNotAccessible.ts, 0, 9))
19+
>super : Symbol(T, Decl(classFieldSuperNotAccessible.ts, 0, 0))
20+
>field : Symbol(T.field, Decl(classFieldSuperNotAccessible.ts, 0, 9))
21+
}
22+
}
23+
24+
new T2().f()
25+
>new T2().f : Symbol(T2.f, Decl(classFieldSuperNotAccessible.ts, 3, 20))
26+
>T2 : Symbol(T2, Decl(classFieldSuperNotAccessible.ts, 2, 1))
27+
>f : Symbol(T2.f, Decl(classFieldSuperNotAccessible.ts, 3, 20))
28+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//// [tests/cases/compiler/classFieldSuperNotAccessible.ts] ////
2+
3+
=== classFieldSuperNotAccessible.ts ===
4+
class T {
5+
>T : T
6+
7+
field = () => {}
8+
>field : () => void
9+
>() => {} : () => void
10+
}
11+
class T2 extends T {
12+
>T2 : T2
13+
>T : T
14+
15+
f() {
16+
>f : () => void
17+
18+
super.field() // error
19+
>super.field() : void
20+
>super.field : () => void
21+
>super : T
22+
>field : () => void
23+
}
24+
}
25+
26+
new T2().f()
27+
>new T2().f() : void
28+
>new T2().f : () => void
29+
>new T2() : T2
30+
>T2 : typeof T2
31+
>f : () => void
32+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// @target: esnext
2+
class A extends class Expr {} {
3+
static {
4+
console.log(super.name);
5+
}
6+
}
7+
class B extends Number {
8+
static {
9+
console.log(super.EPSILON);
10+
}
11+
}
12+
class C extends Array {
13+
foo() {
14+
console.log(super.length);
15+
}
16+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// @target: esnext
2+
class T {
3+
field = () => {}
4+
}
5+
class T2 extends T {
6+
f() {
7+
super.field() // error
8+
}
9+
}
10+
11+
new T2().f()

0 commit comments

Comments
 (0)