Что означает "keyof typeof" в TypeScript и как это работает?
Что такое keyof typeof и как это работает
keyof typeof — это комбинация двух операторов TypeScript, которая позволяет получить тип, представляющий все ключи существующего объекта (а не интерфейса или типа). Давайте разберём каждый оператор по отдельности, а затем посмотрим, как они работают вместе.
Оператор typeof в контексте типов
В TypeScript typeof, используемый в позиции типа (после двоеточия или в угловых скобках), возвращает тип переменной. Например:
const name = "Alice";
type NameType = typeof name; // string
const age = 30;
type AgeType = typeof age; // number
const user = { id: 1, name: "Bob" };
type UserType = typeof user; // { id: number; name: string; }
Обратите внимание: typeof user даёт не просто "object", а точную структуру объекта со всеми типами полей.
Оператор keyof
keyof берёт тип и возвращает объединение (union) всех его ключей. Например:
interface Person {
name: string;
age: number;
email: string;
}
type PersonKeys = keyof Person; // "name" | "age" | "email"
Почему нельзя просто keyof colors?
Потому что keyof работает только с типами, а не со значениями (переменными, константами). colors — это значение (константа), а не тип. Если написать keyof colors, TypeScript выдаст ошибку: 'colors' refers to a value, but is being used as a type here.
Как работает keyof typeof
Конструкция keyof typeof colors работает в два этапа:
typeof colors— получает тип объектаcolors:{ red: string; green: string; blue: string; }keyof typeof colors— берёт ключи этого типа:"red" | "green" | "blue"
В результате ColorKeys будет равен типу "red" | "green" | "blue".
Практический пример использования
Допустим, вы хотите написать функцию, которая принимает ключ объекта colors и возвращает соответствующий цвет:
const colors = {
red: '#ff0000',
green: '#00ff00',
blue: '#0000ff'
};
type ColorKeys = keyof typeof colors; // "red" | "green" | "blue"
function getColor(key: ColorKeys): string {
return colors[key];
}
// Использование
getColor('red'); // OK
getColor('green'); // OK
getColor('yellow'); // Ошибка! Argument of type '"yellow"' is not assignable to parameter of type '"red" | "green" | "blue"'
Без keyof typeof пришлось бы вручную объявлять тип "red" | "green" | "blue", что неудобно и подвержено ошибкам при изменении объекта.
Когда использовать keyof typeof
- Работа с конфигурациями и константами — когда у вас есть объект с фиксированными ключами, и вы хотите безопасно обращаться к его свойствам.
- Создание типов на основе существующих объектов — например, для динамических форм, где ключи объекта определяют поля формы.
- Enum-подобные структуры — когда вы используете объект как альтернативу enum (например, для строковых значений).
Важный нюанс
keyof typeof работает только с объектами, определёнными через const или let. Для интерфейсов и типов (которые и так являются типами) достаточно просто keyof.
interface Config {
apiUrl: string;
timeout: number;
}
type ConfigKeys = keyof Config; // "apiUrl" | "timeout"
// Здесь typeof не нужен, так как Config — уже тип
<<Альтернативный подход: использование as const
Если вы хотите получить не только ключи, но и точные литеральные типы значений (а не просто string), используйте as const вместе с keyof typeof:
const colors = {
red: '#ff0000',
green: '#00ff00',
blue: '#0000ff'
} as const;
type ColorKeys = keyof typeof colors; // "red" | "green" | "blue"
type ColorValues = typeof colors[ColorKeys]; // "#ff0000" | "#00ff00" | "#0000ff"
Без as const тип ColorValues был бы просто string, что менее точно. С as const вы получаете конкретные литеральные строки.
Комбинация с Record
keyof typeof часто используется для создания типов на основе существующих объектов:
const statuses = {
active: 'Активен',
inactive: 'Неактивен',
pending: 'В ожидании'
} as const;
type StatusKey = keyof typeof statuses; // "active" | "inactive" | "pending"
type StatusMap = Record<StatusKey, string>; // { active: string; inactive: string; pending: string; }
// Теперь можно безопасно работать с объектами
function getStatusLabel(status: StatusKey): string {
return statuses[status];
}
Этот подход особенно полезен, когда у вас есть набор фиксированных строковых значений, и вы хотите избежать дублирования кода при определении типов.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги