Извлекает тип, который возвращает функция:
function createUser(name: string, age: number) {
return { id: Date.now(), name, age, active: true }
}
type UserType = ReturnType<typeof createUser>
// { id: number; name: string; age: number; active: boolean }
// Полезно когда у функции нет явной аннотации возвращаемого типа:
type ApiResponse = ReturnType<typeof fetchData>
// С async функциями ReturnType даёт Promise<T>:
async function loadUser(): Promise<User> { /* ... */ }
type LoadResult = ReturnType<typeof loadUser> // Promise<User>
type UserData = Awaited<ReturnType<typeof loadUser>> // UserИзвлекает типы параметров как кортеж:
function greet(name: string, age: number, greeting: string): string {
return `${greeting}, ${name}!`
}
type GreetParams = Parameters<typeof greet>
// [name: string, age: number, greeting: string]
// Получить тип конкретного параметра:
type FirstParam = Parameters<typeof greet>[0] // string
// Практика: тип для функции-обёртки
function withLogging<F extends (...args: any[]) => any>(
fn: F
): (...args: Parameters<F>) => ReturnType<F> {
return (...args) => {
console.log('Вызов с:', args)
return fn(...args)
}
}Как Parameters, но для конструкторов классов:
class Database {
constructor(
private url: string,
private port: number,
private options?: { ssl: boolean }
) {}
}
type DBParams = ConstructorParameters<typeof Database>
// [url: string, port: number, options?: { ssl: boolean }]
// Фабрика через ConstructorParameters:
function createInstance<T extends new (...args: any[]) => any>(
Ctor: T,
...args: ConstructorParameters<T>
): InstanceType<T> {
return new Ctor(...args)
}
const db = createInstance(Database, 'localhost', 5432, { ssl: true })Тип, который создаёт конструктор:
class UserService {
getUser(id: number) { return { id, name: 'Алексей' } }
}
type ServiceInstance = InstanceType<typeof UserService>
// UserService
// Полезно в DI-контейнерах:
type ServiceFactory<T extends new (...args: any[]) => any> = {
create(): InstanceType<T>
singleton: InstanceType<T> | null
}// 1. Debounce с правильными типами:
function debounce<F extends (...args: any[]) => any>(
fn: F,
ms: number
): (...args: Parameters<F>) => void {
let timer: ReturnType<typeof setTimeout>
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => fn(...args), ms)
}
}
// 2. Memoize с правильными типами:
function memoize<F extends (...args: any[]) => any>(
fn: F
): (...args: Parameters<F>) => ReturnType<F> {
const cache = new Map<string, ReturnType<F>>()
return (...args) => {
const key = JSON.stringify(args)
if (!cache.has(key)) cache.set(key, fn(...args))
return cache.get(key)!
}
}
// 3. Curry первого аргумента:
function partial<F extends (first: any, ...rest: any[]) => any>(
fn: F,
first: Parameters<F>[0]
): (...rest: Parameters<F> extends [any, ...infer R] ? R : never) => ReturnType<F> {
return (...rest) => fn(first, ...rest)
}Функции высшего порядка с выводом типов: debounce, memoize, partial, compose — используем Parameters и ReturnType паттерны
// TypeScript: Parameters<F> и ReturnType<F> — извлекают типы параметров и результата.
// В JS реализуем те же паттерны — функции, которые работают с любыми функциями.
// debounce: откладывает вызов до тех пор пока не прекратятся вызовы
function debounce(fn, ms) {
let timer = null
return function (...args) {
clearTimeout(timer)
timer = setTimeout(() => {
timer = null
fn.apply(this, args)
}, ms)
}
}
// throttle: не чаще одного раза за ms миллисекунд
function throttle(fn, ms) {
let lastTime = 0
return function (...args) {
const now = Date.now()
if (now - lastTime >= ms) {
lastTime = now
return fn.apply(this, args)
}
}
}
// memoize: кэширует результаты
function memoize(fn) {
const cache = new Map()
return function (...args) {
const key = JSON.stringify(args)
if (cache.has(key)) return cache.get(key)
const result = fn.apply(this, args)
cache.set(key, result)
return result
}
}
// partial: частичное применение первых N аргументов
function partial(fn, ...preArgs) {
return function (...laterArgs) {
return fn(...preArgs, ...laterArgs)
}
}
// pipe: последовательная композиция функций
function pipe(...fns) {
return function (value) {
return fns.reduce((acc, fn) => fn(acc), value)
}
}
// withRetry: повторяет синхронную функцию при ошибке
function withRetry(fn, attempts = 3) {
return function (...args) {
let lastError
for (let i = 0; i < attempts; i++) {
try {
return fn(...args)
} catch (e) {
lastError = e
console.log(`[retry] попытка ${i + 1} не удалась: ${e.message}`)
}
}
throw lastError
}
}
// --- Демонстрация ---
console.log('=== memoize (аналог Parameters<F>/ReturnType<F>) ===')
let fib_calls = 0
const fibonacci = memoize(function fib(n) {
fib_calls++
if (n <= 1) return n
return fibonacci(n - 1) + fibonacci(n - 2)
})
console.log('fib(10):', fibonacci(10)) // 55
console.log('fib(10):', fibonacci(10)) // 55 (кэш)
console.log('fib(5):', fibonacci(5)) // 5 (кэш)
console.log('Уникальных вызовов:', fib_calls)
console.log('\n=== partial (ConstructorParameters pattern) ===')
function createRequest(baseUrl, endpoint, method, data) {
return { url: baseUrl + endpoint, method, data }
}
// Фиксируем baseUrl — создаём специализированную функцию
const apiRequest = partial(createRequest, 'https://api.example.com')
console.log(apiRequest('/users', 'GET', null))
console.log(apiRequest('/posts', 'POST', { title: 'Hello' }))
// Фиксируем baseUrl + endpoint
const createUser = partial(createRequest, 'https://api.example.com', '/users', 'POST')
console.log(createUser({ name: 'Алексей' }))
console.log('\n=== pipe (compose) ===')
const processText = pipe(
s => s.trim(),
s => s.toLowerCase(),
s => s.replace(/s+/g, '-'),
s => `slug-${s}`
)
console.log(processText(' Hello World ')) // 'slug-hello-world'
console.log(processText('TypeScript Types')) // 'slug-typescript-types'
console.log('\n=== withRetry ===')
let attempt = 0
const unreliable = withRetry(function compute(x) {
attempt++
if (attempt < 3) throw new Error('Временная ошибка')
return x * 2
}, 3)
try {
const result = unreliable(21)
console.log('Результат:', result) // 42
} catch (e) {
console.log('Не удалось:', e.message)
}
console.log('\n=== debounce (демонстрация) ===')
let searchCount = 0
const search = debounce((query) => {
searchCount++
console.log(`Поиск: "${query}" (вызов #${searchCount})`)
}, 100)
// Быстрые вызовы — только последний выполнится
search('T')
search('Ty')
search('Typ')
search('Type') // только этот выполнится
setTimeout(() => {
console.log('Поисков выполнено:', searchCount) // 1
}, 200)Извлекает тип, который возвращает функция:
function createUser(name: string, age: number) {
return { id: Date.now(), name, age, active: true }
}
type UserType = ReturnType<typeof createUser>
// { id: number; name: string; age: number; active: boolean }
// Полезно когда у функции нет явной аннотации возвращаемого типа:
type ApiResponse = ReturnType<typeof fetchData>
// С async функциями ReturnType даёт Promise<T>:
async function loadUser(): Promise<User> { /* ... */ }
type LoadResult = ReturnType<typeof loadUser> // Promise<User>
type UserData = Awaited<ReturnType<typeof loadUser>> // UserИзвлекает типы параметров как кортеж:
function greet(name: string, age: number, greeting: string): string {
return `${greeting}, ${name}!`
}
type GreetParams = Parameters<typeof greet>
// [name: string, age: number, greeting: string]
// Получить тип конкретного параметра:
type FirstParam = Parameters<typeof greet>[0] // string
// Практика: тип для функции-обёртки
function withLogging<F extends (...args: any[]) => any>(
fn: F
): (...args: Parameters<F>) => ReturnType<F> {
return (...args) => {
console.log('Вызов с:', args)
return fn(...args)
}
}Как Parameters, но для конструкторов классов:
class Database {
constructor(
private url: string,
private port: number,
private options?: { ssl: boolean }
) {}
}
type DBParams = ConstructorParameters<typeof Database>
// [url: string, port: number, options?: { ssl: boolean }]
// Фабрика через ConstructorParameters:
function createInstance<T extends new (...args: any[]) => any>(
Ctor: T,
...args: ConstructorParameters<T>
): InstanceType<T> {
return new Ctor(...args)
}
const db = createInstance(Database, 'localhost', 5432, { ssl: true })Тип, который создаёт конструктор:
class UserService {
getUser(id: number) { return { id, name: 'Алексей' } }
}
type ServiceInstance = InstanceType<typeof UserService>
// UserService
// Полезно в DI-контейнерах:
type ServiceFactory<T extends new (...args: any[]) => any> = {
create(): InstanceType<T>
singleton: InstanceType<T> | null
}// 1. Debounce с правильными типами:
function debounce<F extends (...args: any[]) => any>(
fn: F,
ms: number
): (...args: Parameters<F>) => void {
let timer: ReturnType<typeof setTimeout>
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => fn(...args), ms)
}
}
// 2. Memoize с правильными типами:
function memoize<F extends (...args: any[]) => any>(
fn: F
): (...args: Parameters<F>) => ReturnType<F> {
const cache = new Map<string, ReturnType<F>>()
return (...args) => {
const key = JSON.stringify(args)
if (!cache.has(key)) cache.set(key, fn(...args))
return cache.get(key)!
}
}
// 3. Curry первого аргумента:
function partial<F extends (first: any, ...rest: any[]) => any>(
fn: F,
first: Parameters<F>[0]
): (...rest: Parameters<F> extends [any, ...infer R] ? R : never) => ReturnType<F> {
return (...rest) => fn(first, ...rest)
}Функции высшего порядка с выводом типов: debounce, memoize, partial, compose — используем Parameters и ReturnType паттерны
// TypeScript: Parameters<F> и ReturnType<F> — извлекают типы параметров и результата.
// В JS реализуем те же паттерны — функции, которые работают с любыми функциями.
// debounce: откладывает вызов до тех пор пока не прекратятся вызовы
function debounce(fn, ms) {
let timer = null
return function (...args) {
clearTimeout(timer)
timer = setTimeout(() => {
timer = null
fn.apply(this, args)
}, ms)
}
}
// throttle: не чаще одного раза за ms миллисекунд
function throttle(fn, ms) {
let lastTime = 0
return function (...args) {
const now = Date.now()
if (now - lastTime >= ms) {
lastTime = now
return fn.apply(this, args)
}
}
}
// memoize: кэширует результаты
function memoize(fn) {
const cache = new Map()
return function (...args) {
const key = JSON.stringify(args)
if (cache.has(key)) return cache.get(key)
const result = fn.apply(this, args)
cache.set(key, result)
return result
}
}
// partial: частичное применение первых N аргументов
function partial(fn, ...preArgs) {
return function (...laterArgs) {
return fn(...preArgs, ...laterArgs)
}
}
// pipe: последовательная композиция функций
function pipe(...fns) {
return function (value) {
return fns.reduce((acc, fn) => fn(acc), value)
}
}
// withRetry: повторяет синхронную функцию при ошибке
function withRetry(fn, attempts = 3) {
return function (...args) {
let lastError
for (let i = 0; i < attempts; i++) {
try {
return fn(...args)
} catch (e) {
lastError = e
console.log(`[retry] попытка ${i + 1} не удалась: ${e.message}`)
}
}
throw lastError
}
}
// --- Демонстрация ---
console.log('=== memoize (аналог Parameters<F>/ReturnType<F>) ===')
let fib_calls = 0
const fibonacci = memoize(function fib(n) {
fib_calls++
if (n <= 1) return n
return fibonacci(n - 1) + fibonacci(n - 2)
})
console.log('fib(10):', fibonacci(10)) // 55
console.log('fib(10):', fibonacci(10)) // 55 (кэш)
console.log('fib(5):', fibonacci(5)) // 5 (кэш)
console.log('Уникальных вызовов:', fib_calls)
console.log('\n=== partial (ConstructorParameters pattern) ===')
function createRequest(baseUrl, endpoint, method, data) {
return { url: baseUrl + endpoint, method, data }
}
// Фиксируем baseUrl — создаём специализированную функцию
const apiRequest = partial(createRequest, 'https://api.example.com')
console.log(apiRequest('/users', 'GET', null))
console.log(apiRequest('/posts', 'POST', { title: 'Hello' }))
// Фиксируем baseUrl + endpoint
const createUser = partial(createRequest, 'https://api.example.com', '/users', 'POST')
console.log(createUser({ name: 'Алексей' }))
console.log('\n=== pipe (compose) ===')
const processText = pipe(
s => s.trim(),
s => s.toLowerCase(),
s => s.replace(/s+/g, '-'),
s => `slug-${s}`
)
console.log(processText(' Hello World ')) // 'slug-hello-world'
console.log(processText('TypeScript Types')) // 'slug-typescript-types'
console.log('\n=== withRetry ===')
let attempt = 0
const unreliable = withRetry(function compute(x) {
attempt++
if (attempt < 3) throw new Error('Временная ошибка')
return x * 2
}, 3)
try {
const result = unreliable(21)
console.log('Результат:', result) // 42
} catch (e) {
console.log('Не удалось:', e.message)
}
console.log('\n=== debounce (демонстрация) ===')
let searchCount = 0
const search = debounce((query) => {
searchCount++
console.log(`Поиск: "${query}" (вызов #${searchCount})`)
}, 100)
// Быстрые вызовы — только последний выполнится
search('T')
search('Ty')
search('Typ')
search('Type') // только этот выполнится
setTimeout(() => {
console.log('Поисков выполнено:', searchCount) // 1
}, 200)Реализуй функцию `compose(...fns)` — принимает функции f1, f2, ..., fn и возвращает новую функцию, которая применяет их справа налево: compose(f, g, h)(x) = f(g(h(x))). Реализуй функцию `curry(fn)` — превращает функцию с несколькими аргументами в цепочку функций с одним аргументом: curry((a, b, c) => a+b+c)(1)(2)(3) === 6. Реализуй `flip(fn)` — возвращает функцию с переставленными первыми двумя аргументами.
compose: return (x) => fns.reduceRight((acc, fn) => fn(acc), x). curry: function curried(...args) { if (args.length >= fn.length) return fn(...args); return (...more) => curried(...args, ...more) }; return curried. flip: return (a, b, ...rest) => fn(b, a, ...rest).
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке