TypeScript Version: nightly (2.5.0-dev.20170629)
Code
interface I { m(): number; }
const o: I = { m() { throw new Error("not implemented"); } }
Expected behavior:
No error.
Actual behavior:
Type '() => void' is not assignable to type '() => number'.
The same problem occurs inside a class. Manually annotating m(): number fixes the problem, but shouldn't we get that from the contextual type anyway?
Check out #16608 and #8767 for changes/discussion related to this. We could fix this with a voidWideningNever type, or by just checking for a contextual type.
I'm only half-joking.
These should be identical:
const o1: I = { m: function () { throw new Error('nyi'); } }
const o2: I = { m() { throw new Error("not implemented"); } }
@DanielRosenwasser Probably need more input from other people but I think it'd be better to have a flag that undoes #8767 rather than a mashup type of void and never.
I'm actually fine with getting an error in this case:
class Base {
overrideMe() {
throw new Error("You forgot to override me!");
}
}
class Derived extends Base {
overrideMe() {
// Code that actually returns here
}
}
From my perspective, it's better to have to explicitly annotate the return type in the base class, because it might be the case that my to-be-overridden function is actually supposed to return string, or an array, or any number of other things, and an inferred return type of void will just mislead someone who has a reference of type Base.
@masaeedu we talked about this quite a bit last week. The problem with erroring in the above code is that it would be a breaking change. The only reason we even did #8767 was because the number of breaks it induced was too significant.
Breaking change concerns aside, the good thing with giving a type void here is that no one can meaningfully misuse a void value obtained via a Base instance reference. Conversely, you can trivially misuse a never value because it's e.g. a valid function argument to any parameter position. It's worth considering the case where the author of Base does not have any Deriveds in their codebase (because they're shipping a base class library) -- they won't realize they have a "missing" type annotation until one of their consumers shows up complaining.
Re: another flag... this is not something that would be worth doubling the configuration space of TypeScript over.
Most helpful comment
These should be identical: