логотип PurpleSchool
Иконка входа
Вход
  • Обучение
  • Войти
логотип PurpleSchool

Фильтрация undefined в typescript

30 сентября 2022 г.
983 просмотра
фото команды
Автор

Антон

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

изображение курса

Основы разработки

Антон Ларичев
иконка часов4 часа лекций
иконка звёздочки рейтинга5.0
бесплатно
Основы разработки