Indexed Access Types позволяют получить **тип конкретного свойства** из другого типа, используя синтаксис квадратных скобок T[K]. Это похоже на обращение к свойству объекта, но на уровне типов.
interface User {
id: number
name: string
email: string
age: number
}
// Получаем тип конкретного свойства:
type UserName = User['name'] // type UserName = string
type UserId = User['id'] // type UserId = number
type UserAge = User['age'] // type UserAge = numberКомбинация с keyof позволяет получить тип **любого** свойства:
type UserKey = keyof User
// type UserKey = 'id' | 'name' | 'email' | 'age'
type UserValue = User[keyof User]
// type UserValue = number | string
// Объединение всех возможных типов значений
// Практический паттерн — функция getProperty:
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
const user: User = { id: 1, name: 'Алексей', email: 'a@b.com', age: 30 }
const name = getProperty(user, 'name') // тип: string
const id = getProperty(user, 'id') // тип: numberДля массивов можно использовать number как индекс для получения типа элемента:
type Colors = ['red', 'green', 'blue']
type FirstColor = Colors[0] // type FirstColor = 'red'
type SecondColor = Colors[1] // type SecondColor = 'green'
// T[number] — тип любого элемента массива:
type AnyColor = Colors[number] // type AnyColor = 'red' | 'green' | 'blue'
// Особенно полезно с as const:
const ROUTES = ['/home', '/about', '/contact'] as const
type Route = typeof ROUTES[number]
// type Route = '/home' | '/about' | '/contact'interface Config {
server: {
host: string
port: number
ssl: {
enabled: boolean
cert: string
}
}
database: {
url: string
maxConnections: number
}
}
type ServerConfig = Config['server']
// type ServerConfig = { host: string; port: number; ssl: { ... } }
type ServerPort = Config['server']['port']
// type ServerPort = number
type SSLEnabled = Config['server']['ssl']['enabled']
// type SSLEnabled = boolean
type DbUrl = Config['database']['url']
// type DbUrl = stringinterface Product {
id: number
name: string
price: number
category: string
}
// Получить типы нескольких свойств через union:
type ProductStrings = Product['name' | 'category']
// type ProductStrings = string
type ProductNumbers = Product['id' | 'price']
// type ProductNumbers = number// API-функция которая возвращает только нужные поля:
type ApiResponse<T, K extends keyof T> = {
data: Pick<T, K>
status: number
}
// Значение enum из массива:
const PERMISSIONS = ['read', 'write', 'admin'] as const
type Permission = typeof PERMISSIONS[number]
// type Permission = 'read' | 'write' | 'admin'
function hasPermission(user: { permissions: Permission[] }, perm: Permission) {
return user.permissions.includes(perm)
}Indexed access паттерны: динамический доступ к свойствам с проверкой ключей
// В TS: T[K] — получить тип свойства K из типа T
// В JS: показываем runtime-аналог с проверкой корректности ключей
// === Базовый indexed access (getProperty) ===
function getProperty(obj, key) {
// В TS: function getProperty<T, K extends keyof T>(obj: T, key: K): T[K]
if (!(key in obj)) {
throw new Error(`Свойство '${key}' не существует на объекте`)
}
return obj[key]
}
const user = { id: 1, name: 'Алексей', email: 'alex@test.com', age: 30 }
console.log('=== getProperty ===')
console.log(getProperty(user, 'name')) // 'Алексей'
console.log(getProperty(user, 'id')) // 1
console.log(getProperty(user, 'age')) // 30
try {
getProperty(user, 'phone') // В TS: ошибка компиляции. В JS: runtime ошибка
} catch (e) {
console.log(e.message) // "Свойство 'phone' не существует на объекте"
}
// === T[number] — тип элемента массива (через as const паттерн) ===
console.log('\n=== Тип элемента массива ===')
const ROUTES = ['/home', '/about', '/contact', '/profile']
// В TS: type Route = typeof ROUTES[number] = '/home' | '/about' | ...
function isValidRoute(route) {
// runtime проверка того, что TS делает через typeof ROUTES[number]
return ROUTES.includes(route)
}
console.log(isValidRoute('/home')) // true
console.log(isValidRoute('/about')) // true
console.log(isValidRoute('/unknown')) // false
// === Вложенный indexed access ===
console.log('\n=== Вложенный доступ ===')
const config = {
server: { host: 'localhost', port: 3000, ssl: { enabled: false, cert: '' } },
database: { url: 'postgres://...', maxConnections: 10 }
}
// В TS: type ServerPort = Config['server']['port'] = number
function getNestedValue(obj, ...keys) {
return keys.reduce((current, key) => {
if (current == null || !(key in current)) {
throw new Error(`Путь '${keys.join('.')}' не существует`)
}
return current[key]
}, obj)
}
console.log(getNestedValue(config, 'server', 'port')) // 3000
console.log(getNestedValue(config, 'server', 'ssl', 'enabled')) // false
console.log(getNestedValue(config, 'database', 'url')) // 'postgres://...'
// === Получение всех значений объекта (T[keyof T]) ===
console.log('\n=== Все значения объекта ===')
const STATUS = { PENDING: 'pending', ACTIVE: 'active', CLOSED: 'closed' }
// В TS: type StatusValue = typeof STATUS[keyof typeof STATUS] = 'pending' | 'active' | 'closed'
const statusValues = Object.values(STATUS)
console.log('Допустимые статусы:', statusValues) // ['pending', 'active', 'closed']
function validateStatus(status) {
if (!statusValues.includes(status)) {
throw new Error(`Недопустимый статус: ${status}`)
}
return status
}
console.log(validateStatus('active')) // 'active'
try {
validateStatus('unknown')
} catch (e) {
console.log(e.message) // 'Недопустимый статус: unknown'
}Indexed Access Types позволяют получить **тип конкретного свойства** из другого типа, используя синтаксис квадратных скобок T[K]. Это похоже на обращение к свойству объекта, но на уровне типов.
interface User {
id: number
name: string
email: string
age: number
}
// Получаем тип конкретного свойства:
type UserName = User['name'] // type UserName = string
type UserId = User['id'] // type UserId = number
type UserAge = User['age'] // type UserAge = numberКомбинация с keyof позволяет получить тип **любого** свойства:
type UserKey = keyof User
// type UserKey = 'id' | 'name' | 'email' | 'age'
type UserValue = User[keyof User]
// type UserValue = number | string
// Объединение всех возможных типов значений
// Практический паттерн — функция getProperty:
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
const user: User = { id: 1, name: 'Алексей', email: 'a@b.com', age: 30 }
const name = getProperty(user, 'name') // тип: string
const id = getProperty(user, 'id') // тип: numberДля массивов можно использовать number как индекс для получения типа элемента:
type Colors = ['red', 'green', 'blue']
type FirstColor = Colors[0] // type FirstColor = 'red'
type SecondColor = Colors[1] // type SecondColor = 'green'
// T[number] — тип любого элемента массива:
type AnyColor = Colors[number] // type AnyColor = 'red' | 'green' | 'blue'
// Особенно полезно с as const:
const ROUTES = ['/home', '/about', '/contact'] as const
type Route = typeof ROUTES[number]
// type Route = '/home' | '/about' | '/contact'interface Config {
server: {
host: string
port: number
ssl: {
enabled: boolean
cert: string
}
}
database: {
url: string
maxConnections: number
}
}
type ServerConfig = Config['server']
// type ServerConfig = { host: string; port: number; ssl: { ... } }
type ServerPort = Config['server']['port']
// type ServerPort = number
type SSLEnabled = Config['server']['ssl']['enabled']
// type SSLEnabled = boolean
type DbUrl = Config['database']['url']
// type DbUrl = stringinterface Product {
id: number
name: string
price: number
category: string
}
// Получить типы нескольких свойств через union:
type ProductStrings = Product['name' | 'category']
// type ProductStrings = string
type ProductNumbers = Product['id' | 'price']
// type ProductNumbers = number// API-функция которая возвращает только нужные поля:
type ApiResponse<T, K extends keyof T> = {
data: Pick<T, K>
status: number
}
// Значение enum из массива:
const PERMISSIONS = ['read', 'write', 'admin'] as const
type Permission = typeof PERMISSIONS[number]
// type Permission = 'read' | 'write' | 'admin'
function hasPermission(user: { permissions: Permission[] }, perm: Permission) {
return user.permissions.includes(perm)
}Indexed access паттерны: динамический доступ к свойствам с проверкой ключей
// В TS: T[K] — получить тип свойства K из типа T
// В JS: показываем runtime-аналог с проверкой корректности ключей
// === Базовый indexed access (getProperty) ===
function getProperty(obj, key) {
// В TS: function getProperty<T, K extends keyof T>(obj: T, key: K): T[K]
if (!(key in obj)) {
throw new Error(`Свойство '${key}' не существует на объекте`)
}
return obj[key]
}
const user = { id: 1, name: 'Алексей', email: 'alex@test.com', age: 30 }
console.log('=== getProperty ===')
console.log(getProperty(user, 'name')) // 'Алексей'
console.log(getProperty(user, 'id')) // 1
console.log(getProperty(user, 'age')) // 30
try {
getProperty(user, 'phone') // В TS: ошибка компиляции. В JS: runtime ошибка
} catch (e) {
console.log(e.message) // "Свойство 'phone' не существует на объекте"
}
// === T[number] — тип элемента массива (через as const паттерн) ===
console.log('\n=== Тип элемента массива ===')
const ROUTES = ['/home', '/about', '/contact', '/profile']
// В TS: type Route = typeof ROUTES[number] = '/home' | '/about' | ...
function isValidRoute(route) {
// runtime проверка того, что TS делает через typeof ROUTES[number]
return ROUTES.includes(route)
}
console.log(isValidRoute('/home')) // true
console.log(isValidRoute('/about')) // true
console.log(isValidRoute('/unknown')) // false
// === Вложенный indexed access ===
console.log('\n=== Вложенный доступ ===')
const config = {
server: { host: 'localhost', port: 3000, ssl: { enabled: false, cert: '' } },
database: { url: 'postgres://...', maxConnections: 10 }
}
// В TS: type ServerPort = Config['server']['port'] = number
function getNestedValue(obj, ...keys) {
return keys.reduce((current, key) => {
if (current == null || !(key in current)) {
throw new Error(`Путь '${keys.join('.')}' не существует`)
}
return current[key]
}, obj)
}
console.log(getNestedValue(config, 'server', 'port')) // 3000
console.log(getNestedValue(config, 'server', 'ssl', 'enabled')) // false
console.log(getNestedValue(config, 'database', 'url')) // 'postgres://...'
// === Получение всех значений объекта (T[keyof T]) ===
console.log('\n=== Все значения объекта ===')
const STATUS = { PENDING: 'pending', ACTIVE: 'active', CLOSED: 'closed' }
// В TS: type StatusValue = typeof STATUS[keyof typeof STATUS] = 'pending' | 'active' | 'closed'
const statusValues = Object.values(STATUS)
console.log('Допустимые статусы:', statusValues) // ['pending', 'active', 'closed']
function validateStatus(status) {
if (!statusValues.includes(status)) {
throw new Error(`Недопустимый статус: ${status}`)
}
return status
}
console.log(validateStatus('active')) // 'active'
try {
validateStatus('unknown')
} catch (e) {
console.log(e.message) // 'Недопустимый статус: unknown'
}Реализуй функцию `pluck(array, key)`, которая принимает массив объектов и имя ключа, и возвращает массив значений этого ключа. Добавь проверку: если ключ не существует на первом элементе массива — бросать ошибку. В TypeScript это выражается через T[K] — функция возвращает T[K][].
Проверь пустой массив первым: if (array.length === 0) return []. Для проверки ключа используй оператор in: if (!(key in array[0])) throw new Error(...). Затем array.map(item => item[key]).
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке