← Назад к вопросам

Для чего нужен Keyof?

2.2 Middle🔥 182 комментариев
#Soft Skills и рабочие процессы

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Keyof в TypeScript: назначение и применение

Что такое keyof

keyof — это оператор TypeScript, который извлекает ключи объекта в виде типа union. Это мощный инструмент для создания типобезопасного кода, позволяющий убедиться, что ты обращаешься только к существующим свойствам объекта.

interface User {
  id: number
  name: string
  email: string
}

type UserKeys = keyof User // "id" | "name" | "email"

Основной синтаксис

type Keys = keyof SomeType

Результат — union всех ключей типа.

Практические примеры

1. Функция для получения значения свойства

interface Person {
  name: string
  age: number
  city: string
}

// Без keyof — неправильно, может быть любая строка
function getProp(obj: Person, key: string): any {
  return obj[key as keyof Person]
}

// С keyof — типобезопасно
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

const person: Person = { name: 'Alice', age: 30, city: 'NYC' }

const name = getProp(person, 'name') // string (типизирована!)
const age = getProp(person, 'age')   // number (типизирована!)

// Ошибка: 'invalid' не существует в Person
// const invalid = getProp(person, 'invalid')

2. Создание типобезопасного геттера/сеттера

interface Config {
  apiUrl: string
  timeout: number
  debug: boolean
}

class ConfigManager {
  private config: Config = {
    apiUrl: 'https://api.example.com',
    timeout: 5000,
    debug: false
  }

  get<K extends keyof Config>(key: K): Config[K] {
    return this.config[key]
  }

  set<K extends keyof Config>(key: K, value: Config[K]): void {
    this.config[key] = value
  }
}

const manager = new ConfigManager()

manager.set('apiUrl', 'https://new-api.com')      // OK
manager.set('timeout', 10000)                      // OK
manager.set('debug', true)                         // OK

// Ошибка: неправильный тип значения
// manager.set('timeout', 'invalid')

// Ошибка: несуществующий ключ
// manager.set('nonexistent', 'value')

3. Функция forEach с типизацией

interface Product {
  id: number
  name: string
  price: number
  inStock: boolean
}

function forEachKey<T>(obj: T, callback: (key: keyof T, value: T[keyof T]) => void): void {
  Object.entries(obj).forEach(([key, value]) => {
    callback(key as keyof T, value)
  })
}

const product: Product = {
  id: 1,
  name: 'Laptop',
  price: 999,
  inStock: true
}

forEachKey(product, (key, value) => {
  console.log(`${key}: ${value}`)
})

4. Создание объекта с данными формы

interface FormData {
  username: string
  email: string
  password: string
}

class FormBuilder {
  private data: Partial<FormData> = {}

  set<K extends keyof FormData>(field: K, value: FormData[K]): this {
    this.data[field] = value
    return this // Для chaining
  }

  build(): Partial<FormData> {
    return this.data
  }
}

const form = new FormBuilder()
  .set('username', 'alice')           // OK, string
  .set('email', 'alice@example.com')  // OK, string
  .set('password', 'secret123')       // OK, string

// Ошибка: неправильный тип
// form.set('username', 123)

// Ошибка: несуществующий ключ
// form.set('phone', '123-456-7890')

5. Типизированный Object.keys()

interface Settings {
  theme: 'light' | 'dark'
  fontSize: number
  notifications: boolean
}

function getSettingsKeys(): (keyof Settings)[] {
  return Object.keys({} as Settings) as (keyof Settings)[]
}

// или более практично:
const settings: Settings = {
  theme: 'light',
  fontSize: 14,
  notifications: true
}

const keys: (keyof Settings)[] = Object.keys(settings) as (keyof Settings)[]

6. Функция для сортировки объектов по ключам

interface User {
  name: string
  age: number
  email: string
  createdAt: Date
}

function sortByKey<T, K extends keyof T>(items: T[], key: K): T[] {
  return [...items].sort((a, b) => {
    const aVal = a[key]
    const bVal = b[key]

    if (aVal < bVal) return -1
    if (aVal > bVal) return 1
    return 0
  })
}

const users: User[] = [
  { name: 'Bob', age: 30, email: 'bob@example.com', createdAt: new Date() },
  { name: 'Alice', age: 25, email: 'alice@example.com', createdAt: new Date() }
]

const sortedByName = sortByKey(users, 'name')  // OK, sort by string
const sortedByAge = sortByKey(users, 'age')    // OK, sort by number

// Ошибка: несуществующий ключ
// const sorted = sortByKey(users, 'invalid')

Keyof с условными типами

interface TypeA {
  a: string
  b: number
}

interface TypeB {
  x: boolean
  y: string
  z: number
}

// Возвращает ключи, значения которых — string
type StringKeyOf<T> = {
  [K in keyof T]: T[K] extends string ? K : never
}[keyof T]

type StringKeysA = StringKeyOf<TypeA> // "a"
type StringKeysB = StringKeyOf<TypeB> // "y"

Keyof с Generics

// Функция, которая берет объект и возвращает все значения
function getValues<T>(obj: T): T[keyof T][] {
  return Object.values(obj)
}

const config = {
  apiUrl: 'https://api.example.com',
  port: 3000,
  debug: true
}

const values = getValues(config) // (string | number | boolean)[]

Keyof для автодополнения

Одна из главных преимуществ keyof — автодополнение в IDE:

interface Options {
  apiKey: string
  maxRetries: number
  timeout: number
}

// IDE подскажет доступные ключи
function configure<K extends keyof Options>(options: Record<K, Options[K]>): void {
  // ...
}

configure({
  // При вводе вы увидите подсказки: apiKey, maxRetries, timeout
  apiKey: 'secret',
  maxRetries: 3
})

Когда использовать keyof

  1. Типобезопасные геттеры/сеттеры — гарантия что ключ существует
  2. Функции высшего порядка — когда ключ передается как параметр
  3. Валидация данных — проверка что значение соответствует типу ключа
  4. Динамический доступ к свойствам — с гарантией типизации
  5. Автодополнение в IDE — помощь разработчику

Альтернативы

Без keyof (небезопасно)

function getValue(obj: User, key: string): any {
  return obj[key] // Может быть undefined или любой тип
}

С keyof (безопасно)

function getValue<K extends keyof User>(obj: User, key: K): User[K] {
  return obj[key] // Точный тип возвращаемого значения
}

Итого

keyof используется для:

  • Извлечения ключей типа в виде union
  • Создания типобезопасного доступа к свойствам
  • Обеспечения автодополнения в IDE
  • Гарантии что используются только существующие ключи
  • Работы с обобщенными типами (generics)

Это критический инструмент для написания надежного TypeScript кода.