Repository navigation
Overriding an accessor with a property causes runtime error when emitting ESNExt #31225
Description
Activity
j-oliveras commented
on May 3, 2019 ContributorMore actionsI cannot get the error (neither using 3.3.3333 or newer version): I get the same generated result as the link posted by AlCalzone.
Can you post your
tsconfig.jsonand the generated code?I made a mistake in the original reproduction code -- I condensed an example from a more complex problem that I thought would be representative but mistakenly thought that my base class had an
abstractmember. The base class getter is actually implemented, so I updated the example above to correct it. The bug does occur in the updated version, tested by running throughtscthen executing the output innode.The
nodeerror output isTypeError: Cannot set property legs of #
<Animal>which has only a getterSince you asked, I just copied in a tsconfig from another project:
{ "compilerOptions": { "alwaysStrict": true, "allowSyntheticDefaultImports": true, "emitDecoratorMetadata": true, "esModuleInterop": true, "experimentalDecorators": true, "lib": [ "es2016", "dom" ], "module": "esnext", "moduleResolution": "node", "noImplicitAny": true, "noImplicitThis": true, "noUnusedLocals": true, "sourceMap": true, "strictNullChecks": true, "target": "esnext" }, }and the output looks like
"use strict"; class Animal { constructor(name) { this.name = name; } get legs() { return -1; } move(distanceInMeters = 0) { console.log(`${this.name} moved ${distanceInMeters}m.`); } } class Snake extends Animal { get legs() { return 0; } // Correct implementation of abstract superclass property constructor(name) { super(name); } move(distanceInMeters = 5) { console.log("Slithering..."); super.move(distanceInMeters); } } class Horse extends Animal { constructor(name) { 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 = new Horse("Tommy the Palomino"); sam.move(); tom.move(34); //# sourceMappingURL=main.js.map
I just looked at AlCalzone's link. That's very handy thanks! If you make the same change there, giving
Animal#legsan implementation, then run it, you'll see an error similar to the one I posted above. Playing with the options there, it looks like this isn't actually specific to ES6 / native class output. It happens with any target. So, what I thought was a class issue, is actually because TS can't tell the difference between accessor and "normal" properties. I have the sinking feeling there might already been an issue for this, and the answer might be "we can't fix it".Looks like our es6-classes-but-no-class-fields downlevel emit needs a slight change - we need to overwrite the getter/setter on the superclass with the property defined (rather than completely erasing the field). This is probably best done as a followup to #30467 and #30829 as part of overhauling our field semantics to match the spec a bit better.
- addedBugA bug in TypeScriptA bug in TypeScriptES2019Relates to the ES2019 SpecRelates to the ES2019 Specand removed
on May 8, 2019 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?)
Until that feature gets added to the official playground, you could use this playground instead which has that feature:
https://typescript-play.js.org/13 remaining items
- changed the title
[-]Redeclaring an accessor as a property in a subclass causes runtime error when emitting ESNExt[/-][+]Overriding an accessor with a property causes runtime error when emitting ESNExt[/+]on Feb 19, 2020 sandersn commented
on Feb 19, 2020 MemberMore actionsMy current plan is to start issuing these as suggestions with a codefix when class fields reach stage 4. However, the code is error-prone and suspect, so it might be better to
- Always report an error.
- Provide a codefix.
In addition, pretty large corpora of code haven't shown more than a few instances of the error. That means that a codefix is lower priority since the error on its own is sufficient for new code that's being written.
Ron Buckton (@rbuckton) We have plenty of time before 3.9, so maybe we should just turn the error on and see if we run into problems. What do you think?
- addedRescheduledThis issue was previously scheduled to an earlier milestoneThis issue was previously scheduled to an earlier milestone
on Aug 31, 2020 We've been reporting this as an error since at least 4.0. I think we can close this.
Reacted by Nathan Shively-Sanders- addedFixedA PR has been merged for this issueA PR has been merged for this issue
on Dec 15, 2020 - locked as resolved and limited conversation to collaborators
on Oct 21, 2025

TypeScript Version: 3.3.3333 and whatever is on Playground
Search Terms: subclass getter accessor
Code
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
abstractgetter 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 withtarget: 'esnext'but I imagine it should work withtarget: 'es2015'also.