Definindo o type certo para seu array filter
Matheus Moura
Posted on March 15, 2024
Um problema comum que muitos desenvolvedores enfrentam ao utilizar Array.filter
em Typescript, é a utilização de runtime ao invés de type em operações de array.filter
com checagem de nulos.
Exemplo:
// Types
interface IFruitModel {
name: string
icon: string
}
// Models
const banana: IFruitModel = {
name: "Banana",
icon: "🍌"
}
// Data manipulation
const fruits = [banana, null, banana]
const fruitsIcons = fruits
.filter(fruit => !!fruit)
.map(fruit => fruit.icon)
console.log(fruitsIcons)
O código acima irá gerar o erro 'fruit' is possibly 'null'.(18047)
. Isso acontece pois durante o filter
as frutas nulas são removidas, mas o type recebido no map
é IFruitModel | null
.
Durante o map
, temos certeza que não existem frutas nulas, nosso código está correto e temos um erro de tipagem que não faz sentido.
Vou listar abaixo a forma incorreta, e a forma certa de resolver este problema.
🚨 Exemplo do que não fazer
const fruitsIcons = fruits
.filter(fruit => !!fruit)
.map(fruit => fruit as IFruitModel)
.map(fruit => fruit.icon)
Neste caso, é utilizado runtime para corrigir um problema de tipagem, comprometendo a performance do sistema em prol de um simples problema que poderia ser evitado, o código transpilado para JavaScript ficaria assim:
const fruitsIcons = fruits
.filter(fruit => !!fruit)
.map(fruit => fruit)
.map(fruit => fruit.icon)
Perceba que o primeiro map não faz nada a não ser retornar a própria fruta, é um desperdício de processamento.
✅ Exemplo do que fazer
A forma mais adequada de se resolver este problema, é definindo o predicado.
Em um array.filter(predicate: () => boolean)
, o predicado é a função que é passada por parâmetro e sempre retorna um booleano.
Podemos modificar este predicado para injetar um valor de saída no array.filter
da seguinte forma:
const fruitsIcons = fruits
.filter((fruit) : fruit is IFruitModel => !!fruit)
.map(fruit => fruit.icon)
Deste modo a fruta que entra no filter ainda é IFruitModel | undefined
, mas o retorno será um array de IFruitModel
. Viabilizando o map
que vem em seguida.
O código equivalente em javascript seria:
const fruitsIcons = fruits
.filter(fruit => !!fruit)
.map(fruit => fruit.icon)
Posted on March 15, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.