Pesquisa de site

O TypeScript não consegue inferir tipo diferente de undefined através de filter(x => x !== undefined)


O seguinte código TypeScript (4.5.5) gera o erro abaixo:

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

Erro :

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)

Parece que o Typescript não consegue inferir o tipo a partir do filter().
Claro que eu posso "corrigir" isso assim:

.map((key) => data[key].error || ({} as Y))

or 

function foo(o: O): (Y | undefined)[] { ... }

Eu teria pensado que o TS seria capaz de entender o filter(key => !!o[key].y)
Eu perdi alguma coisa?

Resposta #1

Vale a pena usar Object.values em vez de 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);

Parque infantil

Além disso, eu adicionei o predicado withY que funciona como um typeguard.

Os callbacks filter e map são movidos para fora do escopo de foo. Dessa forma, você não precisa inicializar a mesma função arrow quando foo é chamado. Considere isso como uma micro otimização que torna o código mais legível