Description
TypeScript Version: 3.7.0-dev.20191006
Search Terms: covariance, subtype, inference, value type (... honestly I have no idea what the right term for "false
when used as a type" is, it's... probably not this though).
Code:
class Grid<T> {
constructor(width: number, height: number, defaultFill: T) { }
// some function which either behaves as a fill or an in-place equivalent of map, for example
fillWith(valueOrCallback: T | ((current: T, x: number, y: number, idx: number) => T)) {}
}
function takesBoolGrid(grid: Grid<boolean>) { }
// This causes a type error
takesBoolGrid(new Grid(16, 16, false));
// But this doesn't
let g = new Grid(16, 16, false);
takesBoolGrid(g);
Expected behavior: The code above should typecheck. Or, at the very least, both cases should fail, instead of assigning to an intermediate variable fixing the issue.
Actual behavior: I get an error like the following on the first call to takesBoolGrid
:
Argument of type 'Grid<false>' is not assignable to parameter of type 'Grid<boolean>'.
Types of property 'fillWith' are incompatible.
Type '(valueOrCallback: false | ((current: false, x: number, y: number, idx: number) => false)) => void' is not assignable to type '(valueOrCallback: boolean | ((current: boolean, x: number, y: number, idx: number) => boolean)) => void'.
Types of parameters 'valueOrCallback' and 'valueOrCallback' are incompatible.
Type 'boolean | ((current: boolean, x: number, y: number, idx: number) => boolean)' is not assignable to type 'false | ((current: false, x: number, y: number, idx: number) => false)'.
Type 'true' is not assignable to type 'false | ((current: false, x: number, y: number, idx: number) => false)'.(2345)
Related Issues: I looked, but sadly my search terms quickly brought me to a bunch of issues that assume more familiarity with tsc (and type system jargon, probably) than I have. (This is probably because the only terminology I have for describing this problem is, well, type system jargon that I only barely can say I understand).
Some notes that don't fit above which might help, possibly: this code used to work in like, typescript 2.something (I could probably find out which version it was on Monday if it would help).
I updated the code to the new typescript, and got this error, and assumed it was just typescript getting updated with some stricter behavior around variance. I complained online, and was asked to produce an example, and it turned out that the issue was weirder than I had though, as:
- It only errored if the fillWith function was present
- It didn't error if I assign the Grid to an intermediate variable.
Point 2 seems weird, so I decided to file a bug.
Note that (IMO fairly unsurprisingly) the expression takesBoolGrid(new Grid<boolean>(16, 16, false))
type checks fine, e.g. if the type is listed explicitly, no intermediate variable is needed. So I guess it's some kind of type inference thing too? Who knows... (hopefully someone here!)