Антон Ларичев
Типизация, после того как мы с вами применили filter
иногда бывает не такой однозначной как хотелось бы. Этот пост мне навеяло одно ревью.
Итак, представим, что мы продаём чашки для кофе ☕. Не обычные, а какие-нибудь многоразовые, имеющие штрихкод.
export interface CoffeeCup {
id: number;
size: 's' | 'm' | 'l';
color: string;
};
У нас есть сервис, который позволяет нам получить в результате описание нашей чашки по id
, например из базы:
const getCup = (id: number): CoffeeCup | undefined => {
// ...
return;
}
В результате нам может вернуться или чашка кофе или undefined
, так как такой чашки у нас более нет на складе.
Далее в одной из функций мы хотим проверить по id чашки и получить только тем, которые у нас действительно есть в наличии. Ну и конечно мы любим писать код в функциональном стиле, поэтому пишем:
const inStock = [1, 2, 3];
.map(s => getCup(s))
.filter(s => s) // (CoffeeCup | undefined)[]
Казалось бы с точки зрения логики всё хорошо, но с точки зрения типов нет. Мы получаем CoffeeCup
, или undefined
, несмотря на то, что мы всё отфильтровали.
Когда мы принимаем map
, мы из обычных number
получаем (CoffeeCup | undefined)[]
, затем применяя фильтр мы ничего не меняем с точки зрения типов. Для TypeScript удаление каждого элемента типа undefined
, почему-то не приводит к сужению типов. Нам нужно ему помочь.
Решение проблемы - 1
Мы можем явно сделать type assertion к нужному нам типу:
const inStock = [1, 2, 3]
.map(s => getCup(s))
.filter(s => s) as CoffeeCup[] // CoffeeCup []
Тогда мы получим верный тип, но реализация фильтра остаётся на нашей совести, что не очень безопасно с точки зрения типизации. Мы фактически говорим TypeScript, чтобы он игнорировал свои вывод и использовал то, что мы ему дали.
Решение проблемы - 2
Вместо явного указания, мы можем сделать свой type guard. Они по сути позволяют сузить типы за счёт дополнительных проверок:
const inStock = [1, 2, 3]
.map(s => getCup(s))
.filter((s): s is CoffeeCup => !!s) // CoffeeCup []
Обратите внимание на функцию внутри filter
.
(s): s is CoffeeCup => !!s
По сути это type guard, который проверяет что объект является типом CoffeeCup
. В результате мы получаем отфильтрованный объект, где TypeScript будет точно уверен, что его тип не будет undefined
.
Карта развития разработчика
Получите полную карту развития разработчика по всем направлениям: frontend, backend, devops, mobile
Комментарии
0