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

Як описати рекурсивні типи на кшталт JSON чи DeepPartial і які в них обмеження?

Тип може посилатися сам на себе - так описують деревоподібні структури.

Значення JSON:

type Json =
  | string
  | number
  | boolean
  | null
  | Json[]
  | { [key: string]: Json };

const data: Json = { users: [{ id: 1, tags: ['php'], manager: null }] };
const bad: Json = { createdAt: new Date() };   // помилка: Date не є Json

Корисно для функцій, що приймають «будь-який серіалізовний об'єкт» (кеш, localStorage, postMessage) - тоді Date, Map, функції не пройдуть.

Глибокі утиліти:

type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};

type Settings = { theme: { colors: { primary: string; accent: string } } };
const patch: DeepPartial<Settings> = { theme: { colors: { accent: '#f00' } } };

Деревоподібні дані:

type Category = { id: number; name: string; children: Category[] };
type MenuItem = { label: string; href?: string; items?: MenuItem[] };

Обмеження й пастки:

  • функції, дати, Map - T[K] extends object істинне і для них. DeepReadonly<Date> спробує обійти методи Date. Їх виключають окремими гілками: T[K] extends Function | Date | Map<any, any> ? T[K] : ...;
  • масиви в mapped types обробляються як масиви (гомоморфні типи), але кортежі й readonly-масиви інколи потребують окремої обробки;
  • глибина інстанціювання: компілятор обмежує глибину рекурсії. Для дуже глибоких чи «вибухових» типів з'являється «Type instantiation is excessively deep and possibly infinite». Хвостова рекурсія в умовних типах (TS 4.5+) дозволяє до ~1000 рівнів, а звичайна обривається значно раніше - на кількох десятках;
  • продуктивність: рекурсивні типи над великими структурами (типи відповідей API на сотні полів) помітно сповільнюють редактор. Повільна підказка в IDE - ознака, що тип надто «розумний»;
  • рекурсивні псевдоніми (type Json = ... Json[]) підтримуються з TS 3.7; раніше доводилося обходити через інтерфейси.

Порада: рекурсивні типи - для справді рекурсивних даних. Якщо рекурсія потрібна лише для «розумної» перевірки, варто зважити, чи вартий виграш у типах повільнішого редактора й незрозумілих повідомлень про помилки.

Докладніше в документації: Типи з типів

Перевір себе

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

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