Skip to content

Overriding an accessor with a property causes runtime error when emitting ESNExt #31225

Closed
@thw0rted

Description

@thw0rted

TypeScript Version: 3.3.3333 and whatever is on Playground

Search Terms: subclass getter accessor

Code

class Animal {
    public get legs(): number { return -1; }
    constructor(public name: string) { }
    move(distanceInMeters: number = 0) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

class Snake extends Animal {
    public get legs(): number { return 0; }    // Correct implementation of abstract superclass property
    constructor(name: string) { super(name); }
    move(distanceInMeters = 5) {
        console.log("Slithering...");
        super.move(distanceInMeters);
    }
}

class Horse extends Animal {
    public readonly legs: number;    // Not sure if this "should" be a valid redeclaration
    constructor(name: string) {
        super(name);
        this.legs = 4;    // Works when using helper code to emulate pre-ES6 classes, fails with actual ES6
    }
    move(distanceInMeters = 45) {
        console.log("Galloping...");
        super.move(distanceInMeters);
    }
}

let sam = new Snake("Sammy the Python");
let tom: Animal = new Horse("Tommy the Palomino");

sam.move();
tom.move(34);

Expected behavior: Compiler should flag the redeclaration of the getter member as a non-accessor (normal) property, at least when emitting ES6.

Actual behavior: Compiler is fine, but assignment (this.legs = 4) fails at runtime with error about assigning to a member with getter but no setter.

Playground Link: You can paste the above into Playground to see that there are no compile-time errors but the emitted JS does not demonstrate the error because as far as I can tell, you can't tell Playground to emit ES6. (That would be a nice feature; maybe I should put in a ticket for that?)

Related Issues: none that I could find

ETA: I mistakenly had this with an abstract getter to start with. This does not show the buggy behavior because ES6 doesn't have a concept of abstract, so the getter isn't actually defined in the generated parent class at all. The updated code above has an implementation for the getter in the base class and should show the behavior I'm talking about -- I compile it with target: 'esnext' but I imagine it should work with target: 'es2015' also.

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptES2019Relates to the ES2019 SpecFixedA PR has been merged for this issueRescheduledThis issue was previously scheduled to an earlier milestone

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions