Repository navigation
Support override keyword on class methods #2000
Description
Activity
- addedIn DiscussionNot yet reached consensusNot yet reached consensusSuggestionAn idea for TypeScriptAn idea for TypeScript
on Feb 10, 2015 Indeed a good proposal.
However what about the following examples, which are valid overrides to me:
class Snake extends Animal { override move(meters:number, height=-1):void { } }
class A {...} class Animal { setA(a: A): void {...} getA(): A {...} } class B extends A {...} class Snake extends Animal { override setA(a: B): void {...} override getA(): B {...} }
Additionally I would add a compiler flag to force the override keyword to be present (or reported as a warning).
The reason is to catch when renaming a method in a base class that inherited classes already implement (but not supposed to be an override).Reacted by Lucas Basquerotto, stweedie, Shinigami, Nico Jansen, Digory Doo, Karan Alves Pereira, Daniel Shuy, Dušan, Xiaoyi, Artyom Anatolьevich and 15 moreAh nice examples. Generally speaking I would expect the use of the override keyword to enforce exact matching of signatures, as the goal of using it is to maintain a strict typed class hierarchy. So to address your examples:
- Adding an additional default param. This would generate a compile error: Snake super does not define move(meters:number):void. While the derived method is functionally consistent, client code calling Animal.move may not expect derived classes to also be factoring in height (as the base API does not expose it).
- This would (and should always) generate a compile error, as it is not functionally consistent. Consider the following addition to the example:
class C extends A {...} var animal : Animal = new Snake(); animal.setA(new C()); // This will have undefined run-time behavior, as C will be interpreted as type B in Snake.setA
So example (2.) is actually a great demo of how an override keyword can catch subtle edge cases at compile time that would otherwise be missed! :)
And I would again stress that both examples may be valid in specific controlled/advanced javascript scenarios that may be required... in this case users can just choose to omit the override keyword.
Reacted by Joseph Musser, Michał Lytek, Lucas Basquerotto, Mehmet Günaçtı and Alexandre BourdinThis will be useful. We currently work around this by including a dummy reference to the super method:
class Snake extends Animal { move(meters:number, height?:number):void { super.move; // override fix } }
But this only guards against the second case: super methods being renamed. Changes in signature do not trigger a compilation error. Furthermore, this is clearly a hack.
I also don't think that default and optional parameters in the derived class method's signature should trigger a compilation error. That may be correct but goes against the inherent flexibility of JavaScript.
Reacted by Uanela ComoReacted by Joshua Rosen, Gary Kaganas, Max Truxa, Slick Kilmister, Günter Zöchbauer and Paweł JankowskiRowan Wyborn (@rwyborn)
It seems we don't expect the same behaviour.
You would use this override keyword to ensure a same signature, whereas I would use it more as a readibiliy option (so my request to add a compiler option to force its usage).
In fact what I would really expect is that TS detects invalid overriding methods (even without the use of override).
Typically:class Snake extends Animal { move(meters:number, height:number):void {} }should raise an error, because it's really an override of Animal.move() (JS behaviour), but an incompatible one (because height is not supposed to be optional, whereas it will be undefined if called from an Animal "reference").
In fact using override would only confirm (by the compiler) that this method really exists in the base class (and so with a compliant signature, but due to the previous point, not due to the override keyword).stephanedr , speaking as a single user I actually agree with you that the compiler should just always confirm the signature, as I personally like to enforce strict typing within my class hierarchies (even if javascript doesn't!!).
However in proposing that this behavior is optional via the override keyword I am trying to keep in mind that ultimately javascript is untyped, and hence enforcing the strict signature matching by default would result in some javascript design patterns no longer being expressible in Typescript.
Rowan Wyborn (@rwyborn) I'm glad you mentioned the C++ implementation because that's exactly how I imagined it should work before I got here - optionally. Although, a compiler flag that forced the use of the override keyword would go down well in my book.
The keyword would allow compile time errors for a developers clumsy typing, which is what worries me most about overrides in their current form.
class Base { protected commitState() : void { } } class Implementation extends Base { override protected comitState() : void { /// error - 'comitState' doesn't exist on base type } }
Currently (as of 1.4) the
Implementationclass above would just declare a new method and the developer would be none the wiser until they notice their code isn't working.Reacted by Mike Kuenzi, Joseph Musser, Tom, 边城, Reese Walton, Lucas Basquerotto, Masaru Irisawa 入澤 賢 and Bohdan- addedDeclinedThe issue was declined as something which matches the TypeScript visionThe issue was declined as something which matches the TypeScript visionand removedIn DiscussionNot yet reached consensusNot yet reached consensus
on May 4, 2015 RyanCavanaugh commented
on May 4, 2015 MemberMore actionsDiscussed at suggestion review.
We definitely understand the use cases here. The problem is that adding it at this stage in the language adds more confusion than it removes. A class with 5 methods, of which 3 are marked
override, wouldn't imply that the other 2 aren't overrides. To justify its existence, the modifier would really need to divide the world more cleanly than that.Reacted by Nelson Martell and Alexandru PirvuReacted by madhawa priyashantha, Brandon Duffany, edA-qa mort-ora-y, Charlie Harding, David Hearnden, Thomas Hudson, Markus Schneider and Terria KatsuraPlease excuse the whining, but honestly, while your argument does apply to the
publickeyword in a language where everything is public by default, quite world dividing indeed, having things likeabstractand an optionaloverridekeyword will simply help developers feel safer, make less mistakes and waste less time.Overriding is one of the few remaining highly-typo-sensitive aspects of the language, because a mistyped overriding method name is not an obvious compile time problem. The benefit of
overrideis obvious, as it you allows you to state your intention to override - if the base method doesn't exist its a compile time error. All hail the type system. Why would anyone not want this?Reacted by James O'Cull, Joseph Musser, Michał Lytek, Hadrien Milano, Tom, 边城, Lucas Basquerotto, Owen Krafft, Elliot DeNolf, Mike Lynch and 68 moreReacted by James O'Cull, Alexander Bokhankovich, Rich, Anthony Rota, Omer, Michael Ziluck, Felix Honer, Matthew Braithwaite, Michael Sorensen, madhawa priyashantha and 3 moreReacted by madhawa priyashantha, Christoph Linder, groege and edA-qa mort-ora-yReacted by madhawa priyashantha, Christoph Linder, groege and edA-qa mort-ora-yReacted by madhawa priyashantha, Christoph Linder and groegeI concur 100% with Hristo Dachev (@hdachev) , the small inconsistency referred too by Ryan Cavanaugh (@RyanCavanaugh) is easily out weighed by the benefits of the keyword in bringing compile time checks to method overrides. I would again point out that C++ uses an optional override keyword successfully in exactly the same way as suggested for typescript.
I can not emphasize enough how much of a difference override checking makes in a large scale code base with complex OO trees.
Finally I would add that if the inconsistency of an optional keyword really is a concern, then the C# approach could be used, that is the mandatory use of either the "new" or "override" keywords:
class Dervied extends Base { new FuncA(newParam) {} // "new" says that I am implementing a new version of FuncA() with a different signature to the base class version override FuncB() {} // "override" says that I am implementing exactly the same signature as the base class version FuncC() {} // If FuncC exists in the base class then this is a compile error. I must either use the override keyword (I am matching the signature) or the new keyword (I am changing the signature) }
Reacted by Joseph Musser, Tom, Lucas Basquerotto, James, Seth Brenith, Mason, Karol Depka Pradzinski, Melih Yıldız, Daniel Shuy, Marko Mlinaric and 11 moreReacted by Hadrien Milano and Charlie HardingReacted by Patricio Ezequiel Hondagneu Roig, groege and Uanela ComoRyanCavanaugh commented
on May 5, 2015 MemberMore actionsThis isn't analogous to
publicbecause a property without an access modifier is known to be public; a method withoutoverrideis not known to be non-override.Here's a runtime-checked solution using decorators (coming in TS1.5) that produces good error messages with very little overhead:
/* Put this in a helper library somewhere */ function override(container, key, other1) { var baseType = Object.getPrototypeOf(container); if(typeof baseType[key] !== 'function') { throw new Error('Method ' + key + ' of ' + container.constructor.name + ' does not override any base class method'); } } /* User code */ class Base { public baseMethod() { console.log('Base says hello'); } } class Derived extends Base { // Works @override public baseMethod() { console.log('Derived says hello'); } // Causes exception @override public notAnOverride() { console.log('hello world'); } }
Running this code produces an error:
Error: Method notAnOverride of Derived does not override any base class method
Since this code runs at class initialization time, you don't even need unit tests specific to the methods in question; the error will happen as soon as your code loads. You can also sub in a "fast" version of
overridethat doesn't do any checking for production deployments.Reacted by Chris Watson, Michael Scharf, Ketut Sandiarsa, Andreas Pitzer, Michał Krassowski, Nelson Martell, Michael Sorensen, Konstantin Möllers and Francesco Fortin217 remaining items
Load more actionsIf I can share my 2 cents.
Since
overrideis somehow contraintuitive for javascript developers (where all start as public and "overridable" for default). Why don't we think about add afinal?abstract class MyBase { myRegularMethod(): {} final myFinalMethod(): {} } class MyImplementation extends MyBase { myRegularMethod(): { /// Everything works here! 👍 } // Compiler error!!! myFinalMethod is final💀 myFinalMethod(): { } }
That would be backwards compatible.
Because with a final you prevent the use of that method in the extended class as with override that is permitted but you need to explicitly mention your intent and the writer of the super/base method will know that whoever overrides it will make a conscious choice to call the super method or not.
As for the familiarity of javascript developer with
override, that is not necessarily a valid argument. So is the case with other features that TypeScript has over JavaScript and that is the exact reason why it is used. The functionality can however be an option "explcitOverride" that is turned off by defaultReacted by Saninn Salas Diaz, 边城 and Niccolò Maltoniaprilmintacpineda commented
on Jan 19, 2021 More actionsWhat's the latest update? seems like it's still unclear and it's been almost 6 years...
What's the latest update? seems like it's still unclear and it's been almost 6 years...
Actually there's a PR: #39669
Reacted by Junior Dussouillez and Richard LeaReacted by Masaru Irisawa 入澤 賢 and Richard Leaseems its also still planned to be included in the next release: #41601 (at least at the time of writing this)
Reacted by Masaru Irisawa 入澤 賢DanielRosenwasser commented
on Mar 26, 2021 MemberMore actionsThank you Wenlu Wang (@Kingwl), and thank you Paul Cody (@pcj) for the groundwork in the initial proof-of-concept!
Reacted by An Phi, Daniel Chao, Daniel Shuy, Vitalii Shapovalov and Chanakya- addedCommittedThe team has roadmapped this issueThe team has roadmapped this issueFixedA PR has been merged for this issueA PR has been merged for this issueand removedRevisitAn issue worth coming back toAn issue worth coming back to
on Mar 26, 2021 I'm not sure where to ask this, but it seems like
tscis compiling and leaving theoverridekeyword in the compiled result. Is this something other folks are seeing? It seems like it needs the signature to match exactly, or it will show up in the output, see here:abdulkareemnalband commented
on Apr 5, 2021 More actionsAlso
.d.tsfile is missingoverride.
Is it by design? aka for back compat with old ts compilersAlso
.d.tsfile is missingoverride.
Is it by design? aka for back compat with old ts compilersI don't see a reason why
.d.tsfiles would need to contain that keyword; the override check is only useful for each individual class (as it's saying "this class you are writing has methods that don't declare they are overrides"). You can't really do anything with the knowledge that some other class has overriden a method from one of its own parent classes.Leaving "override" in the JS source definitely sounds like a bug though. 🙂
DanielRosenwasser commented
on Apr 5, 2021 MemberMore actionsJake is right. I've opened #43535 to track the JS output bug.
This is a fantastic feature but what about taking it a step farther and requiring use of the
virtualkeyword on the base class so you can actually control when you want to allow sub-classes to override or not? That could also be an opt-in switch,noImplicitVirtual.
There are often situations where overriding a function without callingsupercould break the application.Reacted by Stephen Hicks, Eric Prud'hommeaux, Alex Anderson, whzx5byb, Marvin Heilemann, Uladzimir Mikhalenka, GT Team, Max Smirnov, Dean Haleem, Verity and 3 more
(Update by @RyanCavanuagh)
Please see this comment before asking for "any updates", "please add this now", etc.. Comments not meaningfully adding to the discussion will be removed to keep the thread length somewhat reasonable.
(NOTE, this is not a duplicate of Issue #1524. The proposal here is more along the lines of the C++ override specifier, which makes much more sense for typescript)
An override keyword would be immensely useful in typescript. It would be an optional keyword on any method that overrides a super class method, and similar to the override specifier in C++ would indicate an intent that "the name+signature of this method should always match the name+signature of a super class method". This catches a whole range of issues in larger code bases that can otherwise be easily missed.
Again similar to C++, it is not an error to omit the override keyword from an overridden method. In this case the compiler just acts exactly as it currently does, and skips the extra compile time checks associated with the override keyword. This allows for the more complex untyped javascript scenarios where the derived class override does not exactly match the signature of the base class.
Example use
Example compile error conditions
IntelliSense
As well as additional compile time validation, the override keyword provides a mechanism for typescript intellisense to easily display and select available super methods, where the intent is to specifically override one of them in a derived class. Currently this is very clunky and involves browsing through the super class chain, finding the method you want to override, and then copy pasting it in to the derived class to guarantee the signatures match.
Proposed IntelliSense mechanism
Within a class declaration: