Найти тему

Typescript: Union типы на практике

Представим, что мы пишем свою реализацию “хранилища” данных, которое должно возвращать примерно такое состояние:

interface State<T = unknown> {
status: 'loading' | 'error' | 'success',
error: Error | undefined,
data: T | undefined,
}

На первый взгляд может показаться что всё ок, однако это не совсем так. К примеру, проверка status ничего не скажет нам о состоянии других полей:

if (state.status === 'success') {
console.log(state.data.toFixed());
// Получаем ошибку
// 'state.data' is possibly 'undefined'.
}

Решаем проблему через union тип, добавляя три (по количеству состояний) отдельных интерфейса в каждом из которых error и data напрямую зависят от значения status:

interface LoadingState {
status: 'loading';
error: undefined;
data: undefined;
}

interface ErrorState {
status: 'error';
error: Error;
data: undefined;
}

interface SuccessState<T = unknown> {
status: 'success';
error: undefined;
data: T;
}

type State<T = unknown> =
| LoadingState
| ErrorState
| SuccessState<T>;

Проверяем наш код, и видим что всё работает без ошибок 🎉:

if (state.status === 'success') {
console.log(state.data.toFixed());
}

if (state.status === 'error') {
console.log(state.error.message);
}

Кстати, тоже самое можно провернуть и с типизацией props для React компонента, который должен принимать разный набор параметров в зависимости от значения одного общего.

type GridProps = SharedProps & (GridTypeProps | FlexTypeProps);

Больше информации в Telegram канале https://t.me/around_dev