TypeScript
TypeScript copied to clipboard
A derived class is allowed be created without a parent's getter or setter.
Bug Report
π Search Terms
class extend override getter setter accessor
π Version & Regression Information
- This seems to occur on all versions on the Playground (3.9+)
β― Playground Link
Playground link with relevant code
π» Code
class Parent {
public get f() { return () => {}; }
}
class Child extends Parent {
public set f(func: () => void) { }
}
const c = new Child();
c.f(); // runtime error because Child doesn't implement the getter for f
π Actual behavior
Class Child is allowed to extend class Parent even though, by adding a setter for f, it discards the getter for f and therefore doesn't implement Parent's contract.
π Expected behavior
TypeScript should emit an error because Child is not compatible with Parent.
Sounds like #30852.
@MartinJohns I think you're right, the root of the problem is that when you add a setter, TypeScript assumes you have a getter whether one exists or not, and this is apparently by design.
It'd be nice to fix this but I'm not even sure how. The problem is that this isn't a soundness violation -- if we write out the getter that is implicitly present, there's no problem there either:
class Parent {
public get f() { return () => {}; }
}
class Child extends Parent {
public get(): never {
throw new Error("readonly");
}
public set f(func: () => void) { }
}
An ad-hoc rule that a setter must have a matching getter if there's a property in the base class feels like a hack.
I've convinced myself that the fix for now (if not forever) is to add this eslint rule to require getters and setters to always come in pairs "accessor-pairs": [ "error", { "setWithoutGet": true, "getWithoutSet": true }]
In the following code, typescript expects a boolean, however at runtime it is actually undefined.
class Parent {
protected _value = false;
public get value() { return this._value; }
}
class Child extends Parent {
public set value(value: boolean) { this._value = value; }
}
function f(): boolean {
const obj = new Child();
return obj.value;
}
function g(): true {
const obj = new Child();
obj.value = true;
return obj.value
}
console.log(f()) // undefined
console.log(g()) // undefined
I think that most would expect Child to inherit Parent's getter and behave the same as the following code:
Playground Link
class Parent {
protected _value = false;
public get value() { return this._value; }
}
class Child extends Parent {
public get value() { return this._value; }
public set value(value: boolean) { this._value = value; }
}
function f(): boolean {
const obj = new Child();
return obj.value;
}
function g(): true {
const obj = new Child();
obj.value = true;
return obj.value
}
console.log(f()) // false
console.log(g()) // true
I think that most would expect
Childto inheritParent's getter
Agree completely that itβs expected to inherit the getter. IMO this makes overriding the setter unnecessarily difficult.
just being hit by this issue. The typeScript should error if the parent class has getter/setter but the child only have setter, because in most cases, people's intention is to "inherit" the getter from the parent class.
class Parent {
get a() {
return 1
}
set a(val: number) {}
}
class Child extends Parent {
set a(val: number) {
super.a = val
}
}
const child = new Child()
child.a = 2
console.log('--- after write --')
console.log(child.a)
// ts: child.a is number
// js: child.a is undefined