The following Typescript (4.5.5) code triggers the error below:
interface Y {
prop: string;
}
interface O {
[key: string]: { x: number; y: Y | undefined };
}
function foo(o: O): Y[] {
return Object.keys(o)
.filter((key) => o[key].y !== undefined)
.map((key) => o[key].y);
}
Error:
Type '(Y | undefined)[]' is not assignable to type 'Y[]'.
Type 'Y | undefined' is not assignable to type 'Y'.
Type 'undefined' is not assignable to type 'Y'.ts(2322)
Typescript does not seem to be able to infer the type from the filter().
Of course I can "fix" it by this:
.map((key) => data[key].error || ({} as Y))
or
function foo(o: O): (Y | undefined)[] { ... }
I would have think that TS would be able to understand the filter(key => !!o[key].y)
Did I miss something?
CodePudding user response:
It worth using Object.values
instead of Object.keys
:
interface Y {
prop: string;
}
interface O {
[key: string]: { x: number; y: Y | undefined };
}
const withY = (value: O[string]): value is O[string] & { y: Y } =>
value.y !== undefined
const getY = (value: O[string] & { y: Y }) => value.y
const foo = (o: O): Y[] =>
Object.values(o)
.filter(withY)
.map(getY);
Also, I have added withY
predicate which acts like a typeguard.
filter
and map
callbacks are moved out of foo
scope. In this way you don't need to initialize same arrow function when foo
is called. Consider it as a micro optimization which makes code more readable