TypeScript: Subclass method is not allowed (TS2425) if the parent class has a property of type any with the same name
TypeScript appears to treat class methods as something fundamentally different from a class prototype property of type function. This is not in agreement with the standard semantics of ES6.
TypeScript Version: all versions, as far as I have been able to test.
Search Terms: method member property any parent class subclass
Code
class Super {
prop: any;
}
class Derived extends Super {
prop() {}
}
Expected behavior: should compile without a problem. Note that by the ES6 standard, the above definition of Derived is equivalent to the following, which TypeScript compiles without a problem:
class Derived extends Super {}
Derived.prototype.prop = function(){};
Actual behavior:
Error TS2425: Class 'Super' defines instance member property 'prop', but extended class 'Derived' defines it as instance member function.
Playground Link: https://www.typescriptlang.org/play/#src=class Super { prop%3A any%3B } class Derived extends Super { prop() {} }
Related Issues: I didn’t find similar issues, although I started by looking for issues requesting for the ability to disable particular errors (TS2425 in this case). There are several (closed) issues in which such an ability is requested or suggested, but I believe the proper solution would be to bring TypeScript in agreement with ES6. After all, it claims to be a superset!
About this issue
- Original URL
- State: open
- Created 6 years ago
- Reactions: 12
- Comments: 18 (4 by maintainers)
Commits related to this issue
- [activerecord] Avoiding Promise type definition bugs (error TS2425) https://github.com/Microsoft/TypeScript/issues/27965#issuecomment-430984234 — committed to yukihirop/rue by yukihirop 3 years ago
- [activerecord] Avoiding type definition bugs (error TS2425) https://github.com/Microsoft/TypeScript/issues/27965#issuecomment-430984234 — committed to yukihirop/rue by yukihirop 3 years ago
- Most of the way of static code gen of methods Grr, due to https://github.com/microsoft/TypeScript/issues/27965 — committed to lauckhart/matter.js by lauckhart 8 months ago
I don’t know who came up with this first, but TS and ESnext basically share the same critical mistake to consider a “class field”
equivalent to a constructor initializer
instead of a default property on the prototype.
Besides the fact that this is inconsistent with member functions (which are set on the prototype), this causes all kinds of nasty problems. For example, you can’t override a parent “class field” by setting a derived class prototype property.
If “class fields” were prototype properties instead, there wouldn’t be any problem of this kind (below, I’m avoiding class field syntax in order to avoid confusion).
I think that the current issue ticket is a symptom of this problem being reflected in the TS type system.
At first I thought that adding type checking to JS was a neat idea, but every time I ran into a problem like this (i.e., the TS type system being too incomplete to accurately reflect what’s going on in JS), I became more convinced that a dynamically typed language simply isn’t meant to have static type checking. Either you create a language that is actually statically typed and you can do all the type checking you want (cf. C++), or you create a dynamically typed language, enjoy the added flexibility and accept that you can only do runtime checks. Not to mention the lack of accurate typings for third-party libraries. Type checking isn’t going very well in Python, either.
I’m switching back to CoffeeScript, which has sane “class fields”. This is an important reason for me to switch back, perhaps even the most important.
The problem here is that
prop(){}defined as method is placed on the prototype.prop: anyas property is written directly to the instance (if it had an initializer) and thus overrides the prototype method. Consider the following slightly modified version of your code:You can still use a function to initialize
propinDerived:Some people mention that we can just use an arrow function, like
but this is not ideal because
superin an arrow function to call the same function of a super class).@RyanCavanaugh It seems that a super class having an initializer is the main foot gun that the current restriction aims to prevent.
Can we perhaps have a way to allow overriding properties with prototype methods under the condition that the super class must not have an initializer for the property being overridden (and with strict types therefore requires it to be an optional property)?
For example, what if
is valid with no type error, and
has a type error that says something like “The
Superbase class propertyprophas an initializer, therefore the subclassDerived2can not overridepropwith a prototype method.”I think allowing this under certain conditions would be great!
My specific situation is as follows:
But it also applies if the library were written in TypeScript in the first place.
Fair point. I started this ticket with different assumptions, so the opening post and the title probably don’t accurately reflect anymore what this issue is really about.
I’m not meaning to imply that TypeScript should just accept the example code I presented. Rather, the problem is that there seems to be no alternative. Semantically, it is a valid and even meaningful construction in JavaScript to override a base class method with a subclass non-function prototype property or vice versa. This is especially the case if the return type of the method is the same as the type of the non-function. To me, this seems like a scenario that could be type-checked, but again, there seems no way to do this in TypeScript.
If it hasn’t come up often, then apparently not many Backbone users work with TypeScript. In Backbone,
_.resultis used all over the place. You may be able to gain some ground there!This does not mean “all syntactically-legal ES6 code must not have type errors”; see https://stackoverflow.com/questions/41750390/what-does-all-legal-javascript-is-legal-typescript-mean
This has come up in the past but the specific scenario of “this method may be overriden by a property of a different type” hasn’t come up often enough to warrant a solution
What is
Superdoing here? Do you mean to have an interface instead?