Как извлечь тип свойства интерфейса TypeScript?
Да, это возможно! Используйте индексированный доступ к типу (Indexed Access Types)
Ваша догадка абсолютно верна. В TypeScript есть встроенный механизм для извлечения типа свойства интерфейса — это синтаксис с квадратными скобками, аналогичный доступу к свойствам объекта в JavaScript. Работает это просто и элегантно:
type RoleType = User['role'];
// Результат: 'admin' | 'user' | 'guest'
Это называется индексированный доступ к типу (indexed access type). TypeScript автоматически выводит тип свойства role из интерфейса User и создаёт новый тип, который можно использовать где угодно.
Почему это работает?
В TypeScript интерфейс — это не просто описание, а полноценный тип. Когда вы пишете User['role'], компилятор "заглядывает" внутрь интерфейса и возвращает тип, который соответствует указанному ключу. Это работает для любых свойств:
type UserIdType = User['id']; // number
type UserNameType = User['name']; // string
type UserDateType = User['createdAt']; // Date
Продвинутые примеры использования
- Извлечение нескольких свойств: если нужен тип для нескольких свойств, используйте union ключей:
type NameAndEmail = User['name' | 'email']; // string - Извлечение всех значений: можно получить union всех значений интерфейса:
type AllUserValues = User[keyof User]; // number | string | Date | 'admin' | 'user' | 'guest' - Работа с вложенными объектами: если свойство само является объектом, можно извлечь его вложенные типы:
interface User { profile: { firstName: string; lastName: string; age: number; }; } type ProfileFirstName = User['profile']['firstName']; // string - Создание обобщённых утилит: можно написать функцию, которая принимает объект и возвращает тип его свойства:
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const user: User = { /* ... */ }; const role = getProperty(user, 'role'); // тип: 'admin' | 'user' | 'guest'
Когда это может быть полезно?
- При работе с Redux или другими state-менеджерами, где нужно строго типизировать отдельные части состояния.
- При создании компонентов, принимающих только часть данных из интерфейса.
- При валидации форм, где типы полей должны соответствовать типам в интерфейсе.
- При написании утилит для работы с API, где ответ сервера имеет сложную вложенную структуру.
Этот подход полностью типобезопасен: если вы измените тип свойства в интерфейсе, TypeScript автоматически обновит все типы, которые от него зависят. Никакой дупликации кода!
<<Альтернативный подход: использование условных типов и infer
Если вам нужно извлечь тип свойства из сложного типа или объединения типов, можно использовать условные типы с ключевым словом infer:
type ExtractPropertyType<T, K> = T extends { [P in K]: infer R } ? R : never;
type RoleType = ExtractPropertyType<User, 'role'>;
// 'admin' | 'user' | 'guest'
Этот подход особенно полезен, когда вы работаете с объединениями типов или не знаете точную структуру объекта на момент написания кода. Например:
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; side: number }
| { kind: 'triangle'; base: number; height: number };
type ShapeKind = ExtractPropertyType<Shape, 'kind'>;
// 'circle' | 'square' | 'triangle'
Однако для большинства повседневных задач достаточно простого индексированного доступа User['role']. Условные типы стоит использовать только тогда, когда стандартный подход не работает из-за сложности типов.
Ответов пока нет.
Войдите чтобы оставить ответ