TypeScript Keyof

Ключевое слово 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, который позволяет безопасно и контролируемо работать с ключами объектов и интерфейсов. Его использование повышает типобезопасность и помогает избежать случайных ошибок при обращении к свойствам объектов.