Комментарии (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
- Типобезопасные геттеры/сеттеры — гарантия что ключ существует
- Функции высшего порядка — когда ключ передается как параметр
- Валидация данных — проверка что значение соответствует типу ключа
- Динамический доступ к свойствам — с гарантией типизации
- Автодополнение в 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 кода.