Антон Ларичев
Типизация, после того как мы с вами применили 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.



Комментарии
0