Урок 04

Union типы

Union (|) — тип, который может быть одним из нескольких.

Базовый union

let id: string | number;
id = 'abc';
id = 42;

Union в функциях

function print(value: string | number) {
    console.log(value);
}

Сужение

Чтобы использовать методы конкретного типа — нужно сузить:

function print(value: string | number) {
    if (typeof value === 'string') {
        console.log(value.toUpperCase());
    } else {
        console.log(value.toFixed(2));
    }
}

Union объектов

type Success = { status: 'success'; data: string };
type Error = { status: 'error'; message: string };

type Response = Success | Error;

Дискриминатор

Поле status — общее, но с разными литеральными значениями. По нему TypeScript понимает вариант:

function handle(res: Response) {
    if (res.status === 'success') {
        console.log(res.data);
    } else {
        console.log(res.message);
    }
}

Union с null

type User = {
    name: string;
    email: string | null;
};

if (user.email !== null) {
    console.log(user.email.toLowerCase());
}

Union с undefined

function findUser(id: number): User | undefined {
    return users.find((u) => u.id === id);
}

const user = findUser(1);
if (user) {
    console.log(user.name);
}

Литеральные union

type Status = 'idle' | 'loading' | 'success' | 'error';
type Size = 'sm' | 'md' | 'lg';
type Direction = 'up' | 'down' | 'left' | 'right';

Union vs Enum

// Union
type Status = 'idle' | 'loading';

// Enum
enum Status {
    Idle = 'idle',
    Loading = 'loading',
}

Union проще, не создаёт runtime-код, работает с JSON.

Исключающие проверки

function handle(status: Status) {
    if (status === 'idle') return;
    if (status === 'loading') return;
    // здесь status: 'success' | 'error'
}

Сужение через in

type A = { a: string };
type B = { b: number };

function handle(value: A | B) {
    if ('a' in value) {
        console.log(value.a);
    } else {
        console.log(value.b);
    }
}

Проблема широкого union

function print(value: string | number | boolean | null | undefined) {
    // слишком много вариантов
}

Лучше разбить на несколько функций или использовать discriminated union.

Итоги

  • Union | — одно из нескольких
  • Сужение через typeof, in, дискриминатор
  • Литеральные union для статусов
  • Union лучше enum
  • Discriminated unions — основной паттерн