Проверяет умение типизировать discriminated union с взаимоисключающими полями и выводить типы геттеров через conditional types и keyof.
Когда структура имеет взаимоисключающие поля (например, ответ может быть либо успешным с data, либо ошибочным с error), правильный подход — не делать все поля опциональными, а описать discriminated union: несколько типов, объединённых общим литеральным полем-тегом.
type Success = { kind: 'success'; data: string };
type Failure = { kind: 'failure'; error: Error };
type Result = Success | Failure;
function get<K extends keyof Result>(r: Result, key: K): Result[K] {
return r[key];
}
Здесь keyof Result даёт объединение ключей всех вариантов, а Result[K] — тип значения по ключу. TypeScript сам выведет возвращаемый тип из входного.
Если нужно, чтобы геттер работал только с полями конкретного варианта, используй conditional types и Extract:
type Getter<T, K extends PropertyKey> =
T extends { [P in K]: infer V } ? V : never;
type DataType = Getter<Result, 'data'>; // string
type ErrorType = Getter<Result, 'error'>; // Error
Для варианта, где поля нет, тип схлопывается в never — это защищает от обращения к несуществующему полю.
{ ok: true; value: T } | { ok: false; reason: string }.loading | loaded | error с разными наборами полей.payload зависит от type.Такой подход даёт автодополнение, сужение типов через switch (r.kind) и невозможность обратиться к полю, которого нет в текущем варианте.
Итог: применяй discriminated union вместе с conditional types и keyof, когда структура имеет несколько взаимоисключающих форм — это даёт безопасный вывод типов геттеров без ручного дублирования сигнатур.
Frontend developer
Ментор по Frontend
Полное сопровождение до оффера — без дорогих курсов, с оплатой после трудоустройства
Записаться на консультацию