Home > database >  Typescript cannot infer type not undefined through filter(x => x !== undefined)
Typescript cannot infer type not undefined through filter(x => x !== undefined)

Time:02-11

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);

Playground

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

  • Related