infer — ключевое слово, которое работает только внутри **conditional types** (условных типов). Оно позволяет TypeScript **вывести** и **захватить** часть типа во время сопоставления с образцом, чтобы использовать её в результате.
// Синтаксис:
type MyType<T> = T extends SomeType<infer U> ? U : never
// ^^^^^^
// infer захватывает часть типа в переменную UСамый известный пример использования infer — встроенный ReturnType<T>:
// Как устроен ReturnType изнутри:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never
// ^^^^^^
// infer R захватывает тип возвращаемого значения
function createUser(name: string, age: number) {
return { id: Math.random(), name, age, createdAt: new Date() }
}
type User = ReturnType<typeof createUser>
// type User = { id: number; name: string; age: number; createdAt: Date }// Как устроен Parameters изнутри:
type Parameters<T> = T extends (...args: infer P) => any ? P : never
function sendEmail(to: string, subject: string, body: string): Promise<void> {
return fetch('/api/email', { method: 'POST', body: JSON.stringify({ to, subject, body }) })
.then(() => {})
}
type EmailParams = Parameters<typeof sendEmail>
// type EmailParams = [to: string, subject: string, body: string]// Встроенный Awaited<T> (упрощённо):
type Awaited<T> = T extends Promise<infer U> ? U : T
type Result = Awaited<Promise<string>> // type Result = string
type Result2 = Awaited<Promise<User[]>> // type Result2 = User[]
type Result3 = Awaited<number> // type Result3 = number (не Promise — возвращает как есть)
// Для вложенных Promise:
type DeepAwaited<T> = T extends Promise<infer U> ? DeepAwaited<U> : T
type R = DeepAwaited<Promise<Promise<string>>> // stringtype ElementType<T> = T extends Array<infer U> ? U : never
type Nums = ElementType<number[]> // type Nums = number
type Strs = ElementType<string[]> // type Strs = string
type Never = ElementType<string> // type Never = never (не массив)Первый элемент кортежа:
type Head<T extends any[]> = T extends [infer H, ...any[]] ? H : never
type H1 = Head<[string, number, boolean]> // string
type H2 = Head<[number]> // number
type H3 = Head<[]> // neverПоследний элемент кортежа:
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never
type L1 = Last<[string, number, boolean]> // boolean
type L2 = Last<[string]> // stringИзвлечение типа из Generic:
type UnpackPromise<T> = T extends Promise<infer U> ? U : T
type UnpackArray<T> = T extends Array<infer U> ? U : T
// Можно комбинировать:
type Unpack<T> = T extends Promise<infer U>
? Unpack<U>
: T extends Array<infer V>
? V
: T
type A = Unpack<Promise<string[]>> // stringinfer работает только внутри extends в conditional typesinfer в обычных типахinfer, существует только в ветке true условного типаПаттерны infer в JavaScript: извлечение типов возврата и параметров функций через метапрограммирование
// В TS: infer извлекает типы на уровне типов (compile-time)
// В JS: показываем runtime-аналоги — извлечение информации о функциях
// === ReturnType паттерн: фабрика с отложенным созданием ===
// В TS: ReturnType<typeof createUser> автоматически даёт тип
// В JS: мы знаем что вернёт функция из её вызова
function createUser(name, age) {
return {
id: Math.floor(Math.random() * 1000),
name,
age,
createdAt: new Date().toISOString(),
}
}
function createProduct(name, price, category) {
return {
id: Math.floor(Math.random() * 1000),
name,
price,
category,
inStock: true,
}
}
// "ReturnType" в JS — просто создаём экземпляр и смотрим на ключи:
const userShape = createUser('', 0)
const productShape = createProduct('', 0, '')
console.log('=== Структура User (как ReturnType) ===')
console.log('Ключи User:', Object.keys(userShape))
// ['id', 'name', 'age', 'createdAt']
console.log('Ключи Product:', Object.keys(productShape))
// ['id', 'name', 'price', 'category', 'inStock']
// === Awaited паттерн: разворачивание Promise ===
console.log('\n=== Awaited паттерн ===')
async function fetchUsers() {
// В TS: ReturnType<typeof fetchUsers> = Promise<User[]>
// Awaited<ReturnType<typeof fetchUsers>> = User[]
return [
{ id: 1, name: 'Алексей' },
{ id: 2, name: 'Мария' },
]
}
async function fetchWithRetry(fn, retries) {
for (let i = 0; i < retries; i++) {
try {
// Разворачиваем Promise (как Awaited<T>)
const result = await fn()
return result
} catch (e) {
if (i === retries - 1) throw e
console.log(`Попытка ${i + 1} не удалась, повтор...`)
}
}
}
// Демонстрация async/await как Awaited<Promise<T>>
fetchUsers().then(users => {
console.log('Пользователи:', users.length) // 2
console.log('Тип элемента (как ElementType):', typeof users[0]) // object
})
// === ElementType паттерн: тип элемента массива ===
console.log('\n=== ElementType паттерн ===')
function getFirstElement(arr) {
// В TS: function getFirst<T>(arr: T[]): T | undefined
// infer захватил бы T из T[]
if (arr.length === 0) return undefined
return arr[0]
}
function mapElements(arr, transform) {
// В TS: <T, U>(arr: T[], transform: (item: T) => U): U[]
return arr.map(transform)
}
const numbers = [1, 2, 3, 4, 5]
const strings = ['hello', 'world', 'typescript']
console.log(getFirstElement(numbers)) // 1
console.log(getFirstElement(strings)) // 'hello'
const doubled = mapElements(numbers, x => x * 2)
const upper = mapElements(strings, s => s.toUpperCase())
console.log('Doubled:', doubled) // [2, 4, 6, 8, 10]
console.log('Upper:', upper) // ['HELLO', 'WORLD', 'TYPESCRIPT']
// === Parameters паттерн: мемоизация с автовыводом типов ===
console.log('\n=== Parameters паттерн (мемоизация) ===')
function memoize(fn) {
// В TS: <T extends (...args: any[]) => any>(fn: T): T
// Parameters<T> — тип аргументов, ReturnType<T> — тип результата
const cache = new Map()
return function(...args) {
const key = JSON.stringify(args)
if (cache.has(key)) {
console.log(' [из кэша]')
return cache.get(key)
}
const result = fn(...args)
cache.set(key, result)
return result
}
}
const expensiveCalc = memoize((n) => {
console.log(' [вычисление]')
return n * n
})
console.log(expensiveCalc(5)) // [вычисление] 25
console.log(expensiveCalc(5)) // [из кэша] 25
console.log(expensiveCalc(10)) // [вычисление] 100infer — ключевое слово, которое работает только внутри **conditional types** (условных типов). Оно позволяет TypeScript **вывести** и **захватить** часть типа во время сопоставления с образцом, чтобы использовать её в результате.
// Синтаксис:
type MyType<T> = T extends SomeType<infer U> ? U : never
// ^^^^^^
// infer захватывает часть типа в переменную UСамый известный пример использования infer — встроенный ReturnType<T>:
// Как устроен ReturnType изнутри:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never
// ^^^^^^
// infer R захватывает тип возвращаемого значения
function createUser(name: string, age: number) {
return { id: Math.random(), name, age, createdAt: new Date() }
}
type User = ReturnType<typeof createUser>
// type User = { id: number; name: string; age: number; createdAt: Date }// Как устроен Parameters изнутри:
type Parameters<T> = T extends (...args: infer P) => any ? P : never
function sendEmail(to: string, subject: string, body: string): Promise<void> {
return fetch('/api/email', { method: 'POST', body: JSON.stringify({ to, subject, body }) })
.then(() => {})
}
type EmailParams = Parameters<typeof sendEmail>
// type EmailParams = [to: string, subject: string, body: string]// Встроенный Awaited<T> (упрощённо):
type Awaited<T> = T extends Promise<infer U> ? U : T
type Result = Awaited<Promise<string>> // type Result = string
type Result2 = Awaited<Promise<User[]>> // type Result2 = User[]
type Result3 = Awaited<number> // type Result3 = number (не Promise — возвращает как есть)
// Для вложенных Promise:
type DeepAwaited<T> = T extends Promise<infer U> ? DeepAwaited<U> : T
type R = DeepAwaited<Promise<Promise<string>>> // stringtype ElementType<T> = T extends Array<infer U> ? U : never
type Nums = ElementType<number[]> // type Nums = number
type Strs = ElementType<string[]> // type Strs = string
type Never = ElementType<string> // type Never = never (не массив)Первый элемент кортежа:
type Head<T extends any[]> = T extends [infer H, ...any[]] ? H : never
type H1 = Head<[string, number, boolean]> // string
type H2 = Head<[number]> // number
type H3 = Head<[]> // neverПоследний элемент кортежа:
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never
type L1 = Last<[string, number, boolean]> // boolean
type L2 = Last<[string]> // stringИзвлечение типа из Generic:
type UnpackPromise<T> = T extends Promise<infer U> ? U : T
type UnpackArray<T> = T extends Array<infer U> ? U : T
// Можно комбинировать:
type Unpack<T> = T extends Promise<infer U>
? Unpack<U>
: T extends Array<infer V>
? V
: T
type A = Unpack<Promise<string[]>> // stringinfer работает только внутри extends в conditional typesinfer в обычных типахinfer, существует только в ветке true условного типаПаттерны infer в JavaScript: извлечение типов возврата и параметров функций через метапрограммирование
// В TS: infer извлекает типы на уровне типов (compile-time)
// В JS: показываем runtime-аналоги — извлечение информации о функциях
// === ReturnType паттерн: фабрика с отложенным созданием ===
// В TS: ReturnType<typeof createUser> автоматически даёт тип
// В JS: мы знаем что вернёт функция из её вызова
function createUser(name, age) {
return {
id: Math.floor(Math.random() * 1000),
name,
age,
createdAt: new Date().toISOString(),
}
}
function createProduct(name, price, category) {
return {
id: Math.floor(Math.random() * 1000),
name,
price,
category,
inStock: true,
}
}
// "ReturnType" в JS — просто создаём экземпляр и смотрим на ключи:
const userShape = createUser('', 0)
const productShape = createProduct('', 0, '')
console.log('=== Структура User (как ReturnType) ===')
console.log('Ключи User:', Object.keys(userShape))
// ['id', 'name', 'age', 'createdAt']
console.log('Ключи Product:', Object.keys(productShape))
// ['id', 'name', 'price', 'category', 'inStock']
// === Awaited паттерн: разворачивание Promise ===
console.log('\n=== Awaited паттерн ===')
async function fetchUsers() {
// В TS: ReturnType<typeof fetchUsers> = Promise<User[]>
// Awaited<ReturnType<typeof fetchUsers>> = User[]
return [
{ id: 1, name: 'Алексей' },
{ id: 2, name: 'Мария' },
]
}
async function fetchWithRetry(fn, retries) {
for (let i = 0; i < retries; i++) {
try {
// Разворачиваем Promise (как Awaited<T>)
const result = await fn()
return result
} catch (e) {
if (i === retries - 1) throw e
console.log(`Попытка ${i + 1} не удалась, повтор...`)
}
}
}
// Демонстрация async/await как Awaited<Promise<T>>
fetchUsers().then(users => {
console.log('Пользователи:', users.length) // 2
console.log('Тип элемента (как ElementType):', typeof users[0]) // object
})
// === ElementType паттерн: тип элемента массива ===
console.log('\n=== ElementType паттерн ===')
function getFirstElement(arr) {
// В TS: function getFirst<T>(arr: T[]): T | undefined
// infer захватил бы T из T[]
if (arr.length === 0) return undefined
return arr[0]
}
function mapElements(arr, transform) {
// В TS: <T, U>(arr: T[], transform: (item: T) => U): U[]
return arr.map(transform)
}
const numbers = [1, 2, 3, 4, 5]
const strings = ['hello', 'world', 'typescript']
console.log(getFirstElement(numbers)) // 1
console.log(getFirstElement(strings)) // 'hello'
const doubled = mapElements(numbers, x => x * 2)
const upper = mapElements(strings, s => s.toUpperCase())
console.log('Doubled:', doubled) // [2, 4, 6, 8, 10]
console.log('Upper:', upper) // ['HELLO', 'WORLD', 'TYPESCRIPT']
// === Parameters паттерн: мемоизация с автовыводом типов ===
console.log('\n=== Parameters паттерн (мемоизация) ===')
function memoize(fn) {
// В TS: <T extends (...args: any[]) => any>(fn: T): T
// Parameters<T> — тип аргументов, ReturnType<T> — тип результата
const cache = new Map()
return function(...args) {
const key = JSON.stringify(args)
if (cache.has(key)) {
console.log(' [из кэша]')
return cache.get(key)
}
const result = fn(...args)
cache.set(key, result)
return result
}
}
const expensiveCalc = memoize((n) => {
console.log(' [вычисление]')
return n * n
})
console.log(expensiveCalc(5)) // [вычисление] 25
console.log(expensiveCalc(5)) // [из кэша] 25
console.log(expensiveCalc(10)) // [вычисление] 100Реализуй функцию `memoize(fn)` которая кэширует результаты вызовов функции. При повторном вызове с теми же аргументами должна возвращать кэшированный результат и выводить "[cached]". При первом вызове — вычислять и выводить "[computed]". Ключ кэша — JSON.stringify от массива аргументов.
const cache = new Map(). Ключ: const key = JSON.stringify(args). Проверка: if (cache.has(key)) { console.log("[cached]"); return cache.get(key) }. Вычисление: const result = fn(...args); cache.set(key, result); console.log("[computed]"); return result.
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке