TypeScript Version: 3.0.1 with --strict
class A {
public m(a:string|null): string {
return "";
}
}
class B extends A {
public m(a:string): string {
return "";
}
}
Expected behavior:
Error message
Actual behavior:
No error
B.m override the method A.m so the following code is valid.
let a:A = new B()
a.m(null);
But now B.m is called with null this violates the typing.
Unsound parameter hierarchies in classes are unfortunately extremely common, so class methods are allowed to have their parameters be subtypes or supertypes of their base class's parameters
according to https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html with --strictFunctionTypes parameter type checking is contravariantly. So subtyping a argument is not allowed anymore.
I use the --strict compiler option
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html says "The stricter checking applies to all function types, except those originating in method or constructor declarations."
IIRC that was because the in the wild code that it broke was rather extensive and because we have bad code, this is why we can't have nice things.
this is why we can't have nice things
Indeed; the DOM API itself is unsound in this regard unless you're willing to entertain the notion of write-only properties.
With the response from mattmccutchen I was able find the following issues 18654, 22156, 18963
In #18654 @ahejlsberg wrote:
Methods are excluded specifically to ensure generic classes and interfaces (such as Array
) continue to mostly relate covariantly. The impact of strictly checking methods would be a much bigger breaking change as a large number of generic types would become invariant
(even so, we may continue to explore this stricter mode).
The above example has nothing to do with generic classes and an error that can easily be made especially with --strictNullChecks. Is it not possible to apply strictlyFunctionTypes checking to simple subclassing and not to generic. This is not a perfect solution, but better than now.
For anyone new to this convo, I put together a playground demonstrating this example.
With --strictFunctionTypes enabled, a function with parameter type <S extends T> is not assignable to a function with parameter type <T>, as expected:
function method(callback: (param: number|string) => boolean): void { }
method(function (param: number) { return true }) // error as expected
But method overloading with the same pattern does not give an error.
class Parent {
run(param: number|string): boolean { return true }
}
class Child extends Parent {
run(param: number): boolean { return true } // no error!
}
Most helpful comment
IIRC that was because the in the wild code that it broke was rather extensive and because we have bad code, this is why we can't have nice things.