Шаблонні рядкові типи разом з mapped types дають змогу генерувати API з опису даних - і компілятор перевіряє назви, які раніше були «просто рядками».
Обробники змін для кожного поля стану:
type State = { name: string; age: number; subscribed: boolean };
type ChangeHandlers<T> = {
[K in keyof T & string as `on${Capitalize<K>}Change`]: (value: T[K]) => void;
};
type Handlers = ChangeHandlers<State>;
// {
// onNameChange: (value: string) => void;
// onAgeChange: (value: number) => void;
// onSubscribedChange: (value: boolean) => void;
// }
Типізований емітер подій:
type Events = {
'order:created': { orderId: number };
'order:paid': { orderId: number; amount: number };
'user:logout': undefined;
};
class Emitter<E extends Record<string, unknown>> {
on<K extends keyof E & string>(event: K, handler: (payload: E[K]) => void) { /* ... */ }
emit<K extends keyof E & string>(event: K, payload: E[K]) { /* ... */ }
}
const bus = new Emitter<Events>();
bus.on('order:paid', ({ amount }) => {}); // amount: number
bus.emit('order:payd', { orderId: 1 }); // помилка: друкарська помилка в назві
Вибір підмножини подій за префіксом:
type OrderEvents = Extract<keyof Events, `order:${string}`>; // 'order:created' | 'order:paid'
Ключі перекладів з вкладеного об'єкта:
type Paths<T> = {
[K in keyof T & string]: T[K] extends object ? `${K}.${Paths<T[K]>}` : K;
}[keyof T & string];
const uk = { auth: { login: 'Увійти', logout: 'Вийти' }, nav: { home: 'Головна' } };
type Key = Paths<typeof uk>; // 'auth.login' | 'auth.logout' | 'nav.home'
declare function t(key: Key): string;
t('auth.login'); // ок
t('auth.lgoin'); // помилка
Прийом { ... }[keyof T] - обчислити mapped type і взяти об'єднання всіх значень.
Де межа корисності:
- розмір об'єднань: сотні ключів перекладів на кілька рівнів вкладеності - сповільнення компілятора й редактора. Бібліотеки i18n часто генерують такі типи окремим кроком збирання;
- повідомлення про помилки для складних типів бувають нечитабельними - варто мати прості іменовані проміжні типи;
- динамічні ключі (зібрані з даних під час виконання) не перевіряються - знадобиться приведення типу чи валідація.
Де це приносить найбільшу користь: ключі перекладів, назви подій і каналів, маршрути, CSS-змінні дизайн-системи - рядки, помилки в яких інакше знаходяться лише під час виконання.