Skip to content

Commit f76e01f

Browse files
committed
Disallow property/accessor overrides
Unless the base property or accessor is abstract
1 parent bc7bde3 commit f76e01f

File tree

45 files changed

+1182
-8
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1182
-8
lines changed

src/compiler/checker.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29513,14 +29513,27 @@ namespace ts {
2951329513
// either base or derived property is private - not override, skip it
2951429514
continue;
2951529515
}
29516-
29517-
if (isPrototypeProperty(base) || base.flags & SymbolFlags.PropertyOrAccessor && derived.flags & SymbolFlags.PropertyOrAccessor) {
29518-
// method is overridden with method or property/accessor is overridden with property/accessor - correct case
29516+
if (isPrototypeProperty(base)) {
29517+
// method is overridden with method - correct case
2951929518
continue;
2952029519
}
29521-
2952229520
let errorMessage: DiagnosticMessage;
29523-
if (isPrototypeProperty(base)) {
29521+
const basePropertyFlags = base.flags & SymbolFlags.PropertyOrAccessor;
29522+
const derivedPropertyFlags = derived.flags & SymbolFlags.PropertyOrAccessor;
29523+
if (basePropertyFlags && derivedPropertyFlags) {
29524+
// property/accessor is overridden with property/accessor
29525+
if (!(baseDeclarationFlags & ModifierFlags.Abstract) && basePropertyFlags !== SymbolFlags.Property && derivedPropertyFlags === SymbolFlags.Property) {
29526+
errorMessage = Diagnostics.Class_0_defines_instance_member_accessor_1_but_extended_class_2_defines_it_as_instance_member_property;
29527+
}
29528+
else if (!(baseDeclarationFlags & ModifierFlags.Abstract) && basePropertyFlags === SymbolFlags.Property && derivedPropertyFlags !== SymbolFlags.Property) {
29529+
errorMessage = Diagnostics.Class_0_defines_instance_member_property_1_but_extended_class_2_defines_it_as_instance_member_accessor;
29530+
}
29531+
else {
29532+
// correct case
29533+
continue;
29534+
}
29535+
}
29536+
else if (isPrototypeProperty(base)) {
2952429537
if (derived.flags & SymbolFlags.Accessor) {
2952529538
errorMessage = Diagnostics.Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_accessor;
2952629539
}

src/compiler/diagnosticMessages.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2204,6 +2204,15 @@
22042204
"category": "Error",
22052205
"code": 2609
22062206
},
2207+
"Class '{0}' defines instance member accessor '{1}', but extended class '{2}' defines it as instance member property.": {
2208+
"category": "Error",
2209+
"code": 2610
2210+
},
2211+
"Class '{0}' defines instance member property '{1}', but extended class '{2}' defines it as instance member accessor.": {
2212+
"category": "Error",
2213+
"code": 2611
2214+
},
2215+
22072216
"Cannot augment module '{0}' with value exports because it resolves to a non-module entity.": {
22082217
"category": "Error",
22092218
"code": 2649

src/compiler/utilities.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3972,7 +3972,6 @@ namespace ts {
39723972
}
39733973

39743974
export function getModifierFlagsNoCache(node: Node): ModifierFlags {
3975-
39763975
let flags = ModifierFlags.None;
39773976
if (node.modifiers) {
39783977
for (const modifier of node.modifiers) {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty.ts(5,9): error TS2611: Class 'A' defines instance member property 'p', but extended class 'B' defines it as instance member accessor.
2+
tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty.ts(12,9): error TS2611: Class 'C' defines instance member property 'p', but extended class 'D' defines it as instance member accessor.
3+
4+
5+
==== tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty.ts (2 errors) ====
6+
class A {
7+
p = 'yep'
8+
}
9+
class B extends A {
10+
get p() { return 'oh no' } // error
11+
~
12+
!!! error TS2611: Class 'A' defines instance member property 'p', but extended class 'B' defines it as instance member accessor.
13+
}
14+
class C {
15+
p = 101
16+
}
17+
class D extends C {
18+
_secret = 11
19+
get p() { return this._secret } // error
20+
~
21+
!!! error TS2611: Class 'C' defines instance member property 'p', but extended class 'D' defines it as instance member accessor.
22+
set p(value) { this._secret = value } // error
23+
}
24+
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//// [accessorsOverrideProperty.ts]
2+
class A {
3+
p = 'yep'
4+
}
5+
class B extends A {
6+
get p() { return 'oh no' } // error
7+
}
8+
class C {
9+
p = 101
10+
}
11+
class D extends C {
12+
_secret = 11
13+
get p() { return this._secret } // error
14+
set p(value) { this._secret = value } // error
15+
}
16+
17+
18+
//// [accessorsOverrideProperty.js]
19+
class A {
20+
constructor() {
21+
this.p = 'yep';
22+
}
23+
}
24+
class B extends A {
25+
get p() { return 'oh no'; } // error
26+
}
27+
class C {
28+
constructor() {
29+
this.p = 101;
30+
}
31+
}
32+
class D extends C {
33+
constructor() {
34+
super(...arguments);
35+
this._secret = 11;
36+
}
37+
get p() { return this._secret; } // error
38+
set p(value) { this._secret = value; } // error
39+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
=== tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty.ts ===
2+
class A {
3+
>A : Symbol(A, Decl(accessorsOverrideProperty.ts, 0, 0))
4+
5+
p = 'yep'
6+
>p : Symbol(A.p, Decl(accessorsOverrideProperty.ts, 0, 9))
7+
}
8+
class B extends A {
9+
>B : Symbol(B, Decl(accessorsOverrideProperty.ts, 2, 1))
10+
>A : Symbol(A, Decl(accessorsOverrideProperty.ts, 0, 0))
11+
12+
get p() { return 'oh no' } // error
13+
>p : Symbol(B.p, Decl(accessorsOverrideProperty.ts, 3, 19))
14+
}
15+
class C {
16+
>C : Symbol(C, Decl(accessorsOverrideProperty.ts, 5, 1))
17+
18+
p = 101
19+
>p : Symbol(C.p, Decl(accessorsOverrideProperty.ts, 6, 9))
20+
}
21+
class D extends C {
22+
>D : Symbol(D, Decl(accessorsOverrideProperty.ts, 8, 1))
23+
>C : Symbol(C, Decl(accessorsOverrideProperty.ts, 5, 1))
24+
25+
_secret = 11
26+
>_secret : Symbol(D._secret, Decl(accessorsOverrideProperty.ts, 9, 19))
27+
28+
get p() { return this._secret } // error
29+
>p : Symbol(D.p, Decl(accessorsOverrideProperty.ts, 10, 17), Decl(accessorsOverrideProperty.ts, 11, 35))
30+
>this._secret : Symbol(D._secret, Decl(accessorsOverrideProperty.ts, 9, 19))
31+
>this : Symbol(D, Decl(accessorsOverrideProperty.ts, 8, 1))
32+
>_secret : Symbol(D._secret, Decl(accessorsOverrideProperty.ts, 9, 19))
33+
34+
set p(value) { this._secret = value } // error
35+
>p : Symbol(D.p, Decl(accessorsOverrideProperty.ts, 10, 17), Decl(accessorsOverrideProperty.ts, 11, 35))
36+
>value : Symbol(value, Decl(accessorsOverrideProperty.ts, 12, 10))
37+
>this._secret : Symbol(D._secret, Decl(accessorsOverrideProperty.ts, 9, 19))
38+
>this : Symbol(D, Decl(accessorsOverrideProperty.ts, 8, 1))
39+
>_secret : Symbol(D._secret, Decl(accessorsOverrideProperty.ts, 9, 19))
40+
>value : Symbol(value, Decl(accessorsOverrideProperty.ts, 12, 10))
41+
}
42+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
=== tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty.ts ===
2+
class A {
3+
>A : A
4+
5+
p = 'yep'
6+
>p : string
7+
>'yep' : "yep"
8+
}
9+
class B extends A {
10+
>B : B
11+
>A : A
12+
13+
get p() { return 'oh no' } // error
14+
>p : string
15+
>'oh no' : "oh no"
16+
}
17+
class C {
18+
>C : C
19+
20+
p = 101
21+
>p : number
22+
>101 : 101
23+
}
24+
class D extends C {
25+
>D : D
26+
>C : C
27+
28+
_secret = 11
29+
>_secret : number
30+
>11 : 11
31+
32+
get p() { return this._secret } // error
33+
>p : number
34+
>this._secret : number
35+
>this : this
36+
>_secret : number
37+
38+
set p(value) { this._secret = value } // error
39+
>p : number
40+
>value : number
41+
>this._secret = value : number
42+
>this._secret : number
43+
>this : this
44+
>_secret : number
45+
>value : number
46+
}
47+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty2.ts(6,7): error TS2611: Class 'Base' defines instance member property 'x', but extended class 'Derived' defines it as instance member accessor.
2+
3+
4+
==== tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty2.ts (1 errors) ====
5+
class Base {
6+
x = 1;
7+
}
8+
9+
class Derived extends Base {
10+
get x() { return 2; } // should be an error
11+
~
12+
!!! error TS2611: Class 'Base' defines instance member property 'x', but extended class 'Derived' defines it as instance member accessor.
13+
set x(value) { console.log(`x was set to ${value}`); }
14+
}
15+
16+
const obj = new Derived(); // nothing printed
17+
console.log(obj.x); // 1
18+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//// [accessorsOverrideProperty2.ts]
2+
class Base {
3+
x = 1;
4+
}
5+
6+
class Derived extends Base {
7+
get x() { return 2; } // should be an error
8+
set x(value) { console.log(`x was set to ${value}`); }
9+
}
10+
11+
const obj = new Derived(); // nothing printed
12+
console.log(obj.x); // 1
13+
14+
15+
//// [accessorsOverrideProperty2.js]
16+
class Base {
17+
constructor() {
18+
this.x = 1;
19+
}
20+
}
21+
class Derived extends Base {
22+
get x() { return 2; } // should be an error
23+
set x(value) { console.log(`x was set to ${value}`); }
24+
}
25+
const obj = new Derived(); // nothing printed
26+
console.log(obj.x); // 1
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
=== tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty2.ts ===
2+
class Base {
3+
>Base : Symbol(Base, Decl(accessorsOverrideProperty2.ts, 0, 0))
4+
5+
x = 1;
6+
>x : Symbol(Base.x, Decl(accessorsOverrideProperty2.ts, 0, 12))
7+
}
8+
9+
class Derived extends Base {
10+
>Derived : Symbol(Derived, Decl(accessorsOverrideProperty2.ts, 2, 1))
11+
>Base : Symbol(Base, Decl(accessorsOverrideProperty2.ts, 0, 0))
12+
13+
get x() { return 2; } // should be an error
14+
>x : Symbol(Derived.x, Decl(accessorsOverrideProperty2.ts, 4, 28), Decl(accessorsOverrideProperty2.ts, 5, 23))
15+
16+
set x(value) { console.log(`x was set to ${value}`); }
17+
>x : Symbol(Derived.x, Decl(accessorsOverrideProperty2.ts, 4, 28), Decl(accessorsOverrideProperty2.ts, 5, 23))
18+
>value : Symbol(value, Decl(accessorsOverrideProperty2.ts, 6, 8))
19+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
20+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
21+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
22+
>value : Symbol(value, Decl(accessorsOverrideProperty2.ts, 6, 8))
23+
}
24+
25+
const obj = new Derived(); // nothing printed
26+
>obj : Symbol(obj, Decl(accessorsOverrideProperty2.ts, 9, 5))
27+
>Derived : Symbol(Derived, Decl(accessorsOverrideProperty2.ts, 2, 1))
28+
29+
console.log(obj.x); // 1
30+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
31+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
32+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
33+
>obj.x : Symbol(Derived.x, Decl(accessorsOverrideProperty2.ts, 4, 28), Decl(accessorsOverrideProperty2.ts, 5, 23))
34+
>obj : Symbol(obj, Decl(accessorsOverrideProperty2.ts, 9, 5))
35+
>x : Symbol(Derived.x, Decl(accessorsOverrideProperty2.ts, 4, 28), Decl(accessorsOverrideProperty2.ts, 5, 23))
36+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
=== tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty2.ts ===
2+
class Base {
3+
>Base : Base
4+
5+
x = 1;
6+
>x : number
7+
>1 : 1
8+
}
9+
10+
class Derived extends Base {
11+
>Derived : Derived
12+
>Base : Base
13+
14+
get x() { return 2; } // should be an error
15+
>x : number
16+
>2 : 2
17+
18+
set x(value) { console.log(`x was set to ${value}`); }
19+
>x : number
20+
>value : number
21+
>console.log(`x was set to ${value}`) : void
22+
>console.log : (message?: any, ...optionalParams: any[]) => void
23+
>console : Console
24+
>log : (message?: any, ...optionalParams: any[]) => void
25+
>`x was set to ${value}` : string
26+
>value : number
27+
}
28+
29+
const obj = new Derived(); // nothing printed
30+
>obj : Derived
31+
>new Derived() : Derived
32+
>Derived : typeof Derived
33+
34+
console.log(obj.x); // 1
35+
>console.log(obj.x) : void
36+
>console.log : (message?: any, ...optionalParams: any[]) => void
37+
>console : Console
38+
>log : (message?: any, ...optionalParams: any[]) => void
39+
>obj.x : number
40+
>obj : Derived
41+
>x : number
42+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty3.ts(6,9): error TS2611: Class 'Animal' defines instance member property 'sound', but extended class 'Lion' defines it as instance member accessor.
2+
3+
4+
==== tests/cases/conformance/classes/propertyMemberDeclarations/accessorsOverrideProperty3.ts (1 errors) ====
5+
declare class Animal {
6+
sound: string
7+
}
8+
class Lion extends Animal {
9+
_sound = 'grrr'
10+
get sound() { return this._sound } // error here
11+
~~~~~
12+
!!! error TS2611: Class 'Animal' defines instance member property 'sound', but extended class 'Lion' defines it as instance member accessor.
13+
set sound(val) { this._sound = val }
14+
}
15+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//// [accessorsOverrideProperty3.ts]
2+
declare class Animal {
3+
sound: string
4+
}
5+
class Lion extends Animal {
6+
_sound = 'grrr'
7+
get sound() { return this._sound } // error here
8+
set sound(val) { this._sound = val }
9+
}
10+
11+
12+
//// [accessorsOverrideProperty3.js]
13+
class Lion extends Animal {
14+
constructor() {
15+
super(...arguments);
16+
this._sound = 'grrr';
17+
}
18+
get sound() { return this._sound; } // error here
19+
set sound(val) { this._sound = val; }
20+
}

0 commit comments

Comments
 (0)