Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як зробити повторно використовуваний помічник вичерпної перевірки і як працює switch(true)?

Помічник assertNever - замість того, щоб у кожному switch повторювати присвоєння never:

export function assertNever(value: never, message = 'Неочікуване значення'): never {
  throw new Error(`${message}: ${JSON.stringify(value)}`);
}

type PaymentMethod =
  | { type: 'card'; last4: string }
  | { type: 'bank'; iban: string }
  | { type: 'cash' };

function label(method: PaymentMethod): string {
  switch (method.type) {
    case 'card': return `Картка •${method.last4}`;
    case 'bank': return `Рахунок ${method.iban}`;
    case 'cash': return 'Готівка';
    default: return assertNever(method);
  }
}

Додали { type: 'crypto' } - компілятор вказує на assertNever(method): аргумент більше не never. А під час виконання (дані прийшли з API в обхід типів) - зрозумілий виняток замість тихого undefined.

Без switch - через об'єкт-мапу з повним набором ключів:

const icons = {
  card: 'credit-card',
  bank: 'building-columns',
  cash: 'money-bill',
} satisfies Record<PaymentMethod['type'], string>;

Новий варіант у типі - помилка, що в мапі бракує ключа.

switch (true) зі звуженням (TypeScript 5.3+). Для умов, які не зводяться до одного поля:

function describe(value: string | number | Date | null): string {
  switch (true) {
    case value === null:
      return '-';
    case typeof value === 'string':
      return value.trim();          // value: string
    case typeof value === 'number':
      return value.toFixed(2);      // value: number
    case value instanceof Date:
      return value.toISOString();   // value: Date
    default:
      return assertNever(value);
  }
}

Кожна гілка case звужує тип так само, як if. Це читабельніша альтернатива довгому ланцюжку if/else if.

Помічник match у функціональному стилі - типізований об'єкт обробників:

function match<T extends { type: string }, R>(
  value: T,
  handlers: { [K in T['type']]: (v: Extract<T, { type: K }>) => R },
): R {
  return (handlers as any)[value.type](value);
}

match(method, {
  card: (m) => m.last4,
  bank: (m) => m.iban,
  cash: () => 'готівка',
});   // пропущений обробник - помилка компіляції

Для складних випадків є бібліотека ts-pattern з повноцінним зіставленням зразків і перевіркою вичерпності.

Докладніше в документації: Звуження в switch (true)

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання