flatMap errors when callback returns certain union types
See original GitHub issueTypeScript Version: 3.4.3
Search Terms: flat, flatmap
Code
// Map<string, number[]>
const myMap = new Map([
['foo', [1, 2, 3]],
['bar', [4, 5, 6]]
]);
// string[]
const myArray = ['foo', 'bar', 'baz'];
// (key: any) => number[] | undefined
const mapFn = key => myMap.get(key);
const flatMapped = myArray.flatMap(mapFn);
Actual behavior:
With strict null checks on:
Argument of type '(key: any) => number[] | undefined' is not assignable to parameter of type '(this: undefined, value: string, index: number, array: string[]) => number | readonly number[]'.
Type 'number[] | undefined' is not assignable to type 'number | readonly number[]'.
Type 'undefined' is not assignable to type 'number | readonly number[]'. ts(2345)
Turning strict null checks off prevents the error as you might expect, but that’s obviously not desirable.
More generally, any callback that returns foo[] | bar
(where bar
is not compatible with foo
) seems to run into the same issue. The Map.prototype.get
example I’ve used here just seems like it would be a relatively common use case in particular (you’d typically follow it up by calling .filter(Boolean)
or similar on the mapped result).
More code
Seeing if doing the operations separately will help:
// (number[] | undefined)[]
const mapped = myArray.map(mapFn);
// any[] !!
const flattened = mapped.flat();
Expected behavior:
flatMapped
/flattened
result to be inferred as (number | undefined)[]
.
Playground Link:
Doesn’t seem to be able to load the appropriate library definition, even with /// <reference lib="es2019.array" />
Related Issues:
#29604 seems to be related to the .flat()
part.
Issue Analytics
- State:
- Created 4 years ago
- Comments:8 (4 by maintainers)
Top GitHub Comments
The ES spec prevents almost nothing; see https://stackoverflow.com/questions/41750390/what-does-all-legal-javascript-is-legal-typescript-mean
I think the normal use case for
flatMap
is producing a homogeneous array and the current definition reflects that. To that point, absent other information, the example you posted looks like it has a bug, because probably the author meant to filter out unfound values rather than droppingundefined
s into the resulting array. WritingflatMap<number | undefined>(
to indicate your intent seems like an increase in clarity.I agree - thanks for the tip, I didn’t realize that would work.
I still think there probably exists other “valid” use cases for a heterogeneously mapped array but admit I can’t think of anything particularly compelling right now, and such cases could just do the same as above.
However it does seem a little inconsistent then that plain
map
has no problem with any union type given your position here. Many people’s mental model forflatMap
is that it is simplymap
with the added option of producing multiple elements for a given input element. More to the point, if the normal use case forflatMap
is producing a homogeneous array, then surely that applies tomap
(and I guess by extension, non-tuple arrays in general) too? Is that discrepancy just due to being a technical simplicity/elegance thing?