How to properly override an accessor (getter) in the prototype of a base class with an own property in a derived class? ERROR TS2610

Viewed 996

I have this valid JavaScript code:

class Base {
    // Base has an accesor in the prototype
    get foo() {
        return "getter Base";
    }
}

class Deriv extends Base {
    // I want define here an own property, hiding the access to the getter,
    // but in TypeScript...
    
    // ERROR: 'foo' is defined as an accessor in class 'Base', but is
    // overridden here in 'Deriv' as an instance property. (2610)
    foo = "prop Deriv";
}

Using useDefineForClassFields: true.

What I'm trying to do

Basically I want a base class to have a getter in the prototype and then be able to define an own property (enumerable) in a derived class. I think there are very valid reasons to do that, like having a default algorithm in the base class (or exposing some private state, etc.) but replacing it with a property defined in an instance of a derived class.

TypeScript fails with an error TS2610 as shown in the snippet. I remember seeing the pull request that made this case an error, I don't remember right now the number (think is #37894). I understand this code is using [[Define]] semantics so if I have a setter for that property in the base class it won't be called. But that's exactly what I want!, however TS complains.

What I've tried

Declaration merging with an interface but the error persists:

class Base {
    get foo() { return "getter Base"; }
}

interface Deriv {
    readonly foo: string; // Same error here...
}
class Deriv extends Base {}

Defining the property in the constructor via Object.defineProperty. It works, of course, since TS doesn't check that, but now the types are lying because the derived class is "inheriting" the getter declaration from the base type. For instance the inferred type for o in const o = { ...new Deriv() } doesn't include foo since is a getter in the prototype.

// @ts-ignore. Of course it works, but I think there's no reason to comment more on this...

Using declare readonly foo: string in the derived class. I imagined it wouldn't work but was worth trying. Got same error.

Questions / Final comments

Is there a better way to do this?

If I remember the original PR correctly the rationale behind it was to prevent a developing surprise with the change from [[Set]] to [[Define]] semantics. The issue is that I cannot find a good way to opt-in this behavior with good typings.

Related links:

PR: Always error on property override accessor

Issue: Proposal: allow ambient property declarations to override accessors as well

0 Answers
Related