webroad.online
  1. 1Веб
  2. 2HTML
  3. 3CSS
  4. 4JavaScript
  5. 5TypeScript
  6. 6Git
  7. 7Инструменты
  8. 8React
  9. 9Стейт-менеджмент
  10. 10Next.js
  11. 11Формы
  12. 12Данные и бэкенд
  13. 13SEO
  14. 14Tailwind CSS
  15. 15Анимации
  16. 16Тестирование
  17. 17Архитектура
TypeScript · Урок 3 из 6

Union-типы и сужение типов

Union, литеральные типы, typeof/in/instanceof, discriminated unions и never.

Обновлено

Что такое union type

Union означает: значение может быть одним из нескольких типов.

type Id = string | number
type Status = 'idle' | 'loading' | 'success' | 'error'

Это один из самых используемых инструментов TS: статусы, варианты компонентов, ответы, которые могут быть успехом или ошибкой.

Что такое сужение типов

Если значение string | number, TS позволяет использовать только то, что общее у обоих. Чтобы вызвать методы строки, нужно доказать, что это строка. Это и есть сужение (narrowing): if, после которого TS знает точный тип.

function format(id: string | number) {
  id.toUpperCase()                 // ✗ у number нет toUpperCase
  if (typeof id === 'string') {
    return id.toUpperCase()        // ✓ здесь TS знает: string
  }
  return id.toFixed(0)             // ✓ здесь: number
}

Виды сужения — и когда

Проверка Для Пример
typeof x === 'string' примитивов string | number
x === null, x !== undefined, if (x) отсутствующих значений User | null
'key' in x объектов разной формы Cat | Dog
x instanceof Error классов catch (e)
Array.isArray(x) массив или что-то другое string | string[]
x.kind === 'circle' discriminated unions см. ниже
функция x is T переиспользуемая проверка isUser(x)

Discriminated unions — самый полезный паттерн

У каждого варианта есть общее поле с разным литеральным значением (дискриминант). switch по нему сужает тип автоматически:

type Result =
  | { status: 'success'; data: User }
  | { status: 'error'; message: string }
  | { status: 'loading' }

function render(r: Result) {
  switch (r.status) {
    case 'success': return r.data.name      // TS знает, что data есть
    case 'error': return r.message          // здесь message
    case 'loading': return 'Загрузка…'
  }
}

Прочитать data в состоянии ошибки невозможно — исчезает целый класс багов. Тем же паттерном описывают действия reducer-а в React.

Исчерпывающая проверка через never

Если позже добавите { status: 'empty' }, хочется, чтобы TS показал все switch, которые его не обрабатывают:

default: {
  const unreachable: never = r      // ✗ ошибка, если остался необработанный вариант
  return unreachable
}

Type predicates

Функция, которая проверяет тип и «сообщает» TS результат:

function isString(x: unknown): x is string {
  return typeof x === 'string'
}

const values: unknown[] = ['a', 1, 'b']
const strings = values.filter(isString)   // string[]

Коротко

  • Union = «одно из»; литеральные union — для статусов и вариантов.
  • Сужение = typeof, in, instanceof, равенство — после if TS знает тип.
  • Discriminated unions + switch + never = код без невозможных состояний.

Официальные источники

Упражнения

Была ли страница полезной?

Один клик — без регистрации.