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, равенство — послеifTS знает тип. - Discriminated unions +
switch+never= код без невозможных состояний.