Cannot pass Array<T> to a function expecting Array<?T>
silverlyra opened this issue · comments
I just discovered some behavior regarding arrays of maybe types that I found surprising:
const lengths = (strings: Array<?string>): Array<number> =>
strings.map((s) => s ? s.length : 0)
let strings: Array<string> = ['hello', 'world']
lengths(strings) // Flow error
An Array<T>
cannot be passed as an argument for a parameter of type Array<?T>
, even though all values in an Array<T>
are valid in an Array<?T>
. 👉 try flow
Casting via any
works, but is nasty. Is this expected behavior? Is there a workaround other than casting to any
?
I'm sorry if this is already a reported issue; I couldn't find it.
I believe the issue is that if the function takes strings: Array<?string>
then you're allowed to, e.g., do strings.push(null)
, which is invalid on an Array<string>
.
If the necessary subtyping were allowed, then lengths
could corrupt the type of externalStrings
, e.g.
const lengths = (strings: Array<?string>): Array<number> => {
strings[0] = null; // I've just corrupted `externalStrings`
strings.map((s) => s ? s.length : 0);
}
let externalStrings: Array<string> = ['hello', 'world']
lengths(externalStrings) // Flow error
You can use $ReadOnlyArray now apparently, though not sure if it's documented:
https://github.com/facebook/flow/blob/v0.48.0/lib/core.js#L185
#3425 (comment)
I believe the issue is that if the function takes
strings: Array<?string>
then you're allowed to, e.g., dostrings.push(null)
, which is invalid on anArray<string>
.
ahhhhhhhh. right. mutability. never caused anyone any problems. 🙄
thank you – that makes perfect sense. $ReadOnlyArray
is perfect for my use case.