Add override / noImplicitOverride support for interfaces
Suggestion
🔍 Search Terms
noImplicitOverride interface override
✅ Viability Checklist
My suggestion meets these guidelines:
- [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
- [x] This wouldn't change the runtime behavior of existing JavaScript code
- [x] This could be implemented without emitting different JS based on the types of the expressions
- [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- [x] This feature would agree with the rest of TypeScript's Design Goals.
⭐ Suggestion
📃 Motivating Example
The new override keyword in TS4.3 works for classes but not for interfaces.
Would be great to have it also available for interfaces. As interfaces are technically not overridden, alternatively a new keyword like implement could be introduced (Java, for example, uses override for classes and interfaces though)
💻 Use Cases
What is the advantage of override / noImplicitOverride for interfaces? Consider this case:
interface ISomething {
doSomething?: () => void;
}
class Something implements ISomething {
doSomething: () => {console.log("Something")}
}
If you rename doSomething() in the interface to doSomethingElse(), this would not throw an TS error, since it is optional and therefore doesn't have to be implemented. The implementing class would still have a doSomething() method which never gets executed.
If I would be forced to do something like this, TS could throw an error if I rename doSomething in the interface:
class Something implements ISomething {
override doSomething: () => {console.log("Something")}
}
Currently I am using a custom TSLint rule via eslint but TSLint is already deprecated and also the rule stopped working with TS 4.3. An according ESLint is not planned for this. Anyhow, I think this check could be done directly in TS.
/cc: @DanielRosenwasser @sandersn
Although this is intentional:
https://github.com/microsoft/TypeScript/issues/44439#issuecomment-856310109
It's intentional that
overridedoesn't apply toimplementsclauses, since you're not overriding anything.
I want to be able to override even just for documentation:
interface A {
/**
* Bla-bla.
*/
doStuff: () => void;
}
interface B extends A {
/**
* Bla-bla, but in the context of B: additional concerns and ramifications etc.
*/
override doStuff: () => void;
}
This would also help to notice possibly unused exported methods when an interface is removed from the implements clause and the method is no longer necessary.
yup this makes sense. could reuse the implements keyword.
It's intentional that
overridedoesn't apply toimplementsclauses, since you're not overriding anything.
Imo it's totally fine to re-use override for this.
It may not be technically correct but at least it's consistent, easily understandable and doesn't require any code changes when you replace an interface with a base class for example.
on second thought, I realize why this unpopular:
The override keyword is mainly a backup for when "something bad happens" and doesn't provide any direct value.
Perhaps we can move this to JSdocs? There is already some cross-compatibility between JSdocs and TS, maybe we can use the @implements syntax?
- clears ambiguity with more-than-one base interface
- causes less disturbance when reading the code
- can still be enforced, and auto completed
class Cat implements Animal {
/** @implements Animal */
clone() { return new Cat(this.name); }
}
Note that this is exactly how Kotlin already does this: whenever you implement a method, you'd add the override keyword for every method you're implementing from the interface. The only difference is that this is actually required in Kotlin, but we can solve that by either extending the noImplicitOverride flag to also apply to interfaces or add a new flag.
Folks, don't forget to vote for this request! :handshake:
Just wanted to emphasize the original poster's point that Java has had it since 2006, long before Kotlin was even a dream :D However, I love the idea of making it mandatory.
In addition, I would love more type inference. For example, this doesn't work:
interface ITest {
test(arg: string): number
}
class Test implements ITest {
test(arg) { // Parameter 'arg' implicitly has an 'any' type.(7006)
return arg.length
}
}
While it seems to be the right (and unfortunate) thing for TypeScript, something like the override keyword could help there.
I feel it would be a great built-in feature to improve documentation and detect correctness. (More weight on the documentation side in complex classes)
Without this feature, it is difficult to use type guards effectively.
interface ITest {
mySpecialProperty: true;
doSth(): number;
}
function isITest(obj: any): obj is ITest {
return obj?.mySpecialProperty === true;
}
class Foo implements ITest {
mySpecialProperty = true as const;
doSth() {
return 42;
}
}
const foo = new Foo();
if (isITest(foo)) {
console.log(foo.doSth());
}
Eventually, ITest is removed from Foo, but the property used for the type guard is overlooked.
class Foo {
mySpecialProperty = true as const;
}
const foo = new Foo();
if (isITest(foo)) {
console.log(foo.doSth()); //Error
}
If we had override, this could not happen. If we do not get runtime information about interfaces, at least let us make sure that stuff works at design time
I don't see any downsides to this and it makes and helps keeping the code more clean.