Ключевое слово keyof в TypeScript позволяет извлекать ключи (названия свойств) из объекта или интерфейса и создавать на их основе новый тип. Чаще всего это используется для обеспечения безопасности при доступе к свойствам объекта, ведь с помощью keyof можно ограничить набор допустимых ключей, предотвращая случайные ошибки.
Как работает keyof?
Когда мы используем keyof с типом объекта, результатом будет объединение всех ключей этого объекта. Это объединение представляет собой набор всех возможных ключей, которые можно использовать для обращения к свойствам объекта.
keyof с явными ключами
Когда keyof применяется к объектному типу с явными ключами, он создаёт объединённый тип (union type) из этих ключей.
Использование с интерфейсом
Допустим, у нас есть интерфейс с двумя свойствами.
Пример
interface Person {
name: string;
age: number;
}
// Тип keyof Person будет: "name" | "age"
type PersonKeys = keyof Person; // "name" | "age"
// Попробуем обратиться к свойству объекта с помощью ключа
const person: Person = { name: "Максим", age: 30 };
// Правильно
const name: PersonKeys = "name";
console.log(person[name]); // Максим
// Неправильно (ошибка компиляции)
const invalidKey: PersonKeys = "height"; // Ошибка: "height" не принадлежит "name" | "age"
Использование с объектами
Тот же эффект наблюдается и при работе с обычными объектами.
Пример
const user = {
email: "user@example.com",
role: "admin"
};
// Тип keyof typeof user будет: "email" | "role"
type UserKeys = keyof typeof user; // "email" | "role"
// Используем ключ, чтобы получить значение
const key: UserKeys = "email";
console.log(user[key]); // user@example.com
Применение keyof в функциях
keyof очень удобно использовать для ограничения возможного набора ключей, передаваемых в функцию. Например, хотим создать функцию, которая принимает объект и его свойство, и выводит значение этого свойства.
Пример
interface Person {
name: string;
age: number;
}
function getProperty<T, K extends keyof T>(obj: T, prop: K): T[K] {
return obj[prop];
}
const person: Person = { name: "Максим", age: 30 };
// Правильно
console.log(getProperty(person, "name")); // Максим
// Неправильно (ошибка компиляции)
console.log(getProperty(person, "gender")); // Ошибка: gender не принадлежит "name" | "age"
Здесь мы видим, что благодаря использованию keyof, функция getProperty гарантирует, что мы обращаемся только к реально существующим свойствам объекта.
Совместное использование с условными типами
Тип keyof прекрасно сочетается с условными типами (Об условных типах см. соответствующую главу). Например, мы можем создать тип, который возвращает только те свойства объекта, которые имеют определенный тип:
Пример
type GetNumberProps<T> = {
[K in keyof T]: T[K] extends number ? K : never;
}[keyof T];
interface MixedProps {
name: string;
age: number;
active: boolean;
height: number;
}
// Result: "age" | "height"
type NumberProps = GetNumberProps<MixedProps>; // "age" | "height"
keyof с индексированными сигнатурами
keyof также можно использовать с индексированными сигнатурами для извлечения типа индекса.
Пример
type StringMap = { [key: string]: unknown };
// `keyof StringMap` здесь будет типа `string`
function createStringPair(property: keyof StringMap, value: string): StringMap {
return { [property]: value };
}
Когда использовать keyof?
- Когда нужно ограничить доступ к свойствам объекта, чтобы избежать обращений к несуществующим ключам.
- При создании универсальных функций, работающих с любыми объектами и их свойствами.
- Для обеспечения большей типобезопасности при работе с ключами объектов.
Ключевое слово keyof — важный инструмент TypeScript, который позволяет безопасно и контролируемо работать с ключами объектов и интерфейсов. Его использование повышает типобезопасность и помогает избежать случайных ошибок при обращении к свойствам объектов.