34. ReturnType, Parameters
TypeScript: Броня. Урок 33: ReturnType и Parameters
Заголовок раздела «TypeScript: Броня. Урок 33: ReturnType и Parameters»ReturnType и Parameters - это встроенные utility types для извлечения типов из функциональных сигнатур. Они критически важны для работы с higher-order functions, декораторами и type inference в сложных сценариях.
ReturnType<T>
Заголовок раздела «ReturnType<T>»ReturnType<T> извлекает тип возврата функции:
// Определение ReturnType (встроенное)type ReturnType<T extends (...args: any) => any> = T extends ( ...args: any) => infer R ? R : any;
// Простой примерfunction getUser() { return { id: '123', name: 'Alice', };}
type User = ReturnType<typeof getUser>;// { id: string; name: string; email: string }
// С generic функциямиfunction createArray<T>(value: T): T[] { return [value];}
type ArrayResult = ReturnType<typeof createArray<number>>;// number[]
// С async функциямиasync function fetchData() { return { data: 'value' };}
type FetchResult = ReturnType<typeof fetchData>;// Promise<{ data: string }>
// Чтобы получить unwrapped тип, используйте Awaitedtype UnwrappedFetchResult = Awaited<ReturnType<typeof fetchData>>;// { data: string }Parameters<T>
Заголовок раздела «Parameters<T>»Parameters<T> извлекает типы параметров функции как tuple:
// Определение Parameters (встроенное)type Parameters<T extends (...args: any) => any> = T extends ( ...args: infer P) => any ? P : never;
// Простой примерfunction addNumbers(a: number, b: number): number { return a + b;}
type AddParams = Parameters<typeof addNumbers>;// [a: number, b: number]
// Доступ к отдельным параметрамtype FirstParam = Parameters<typeof addNumbers>[0]; // numbertype SecondParam = Parameters<typeof addNumbers>[1]; // number
// С rest parametersfunction log(message: string, ...tags: string[]): void { console.log(message, tags);}
type LogParams = Parameters<typeof log>;// [message: string, ...tags: string[]]
// С optional parametersfunction greet(name: string, greeting?: string): string { return `${greeting || 'Hello'}, ${name}!`;}
type GreetParams = Parameters<typeof greet>;// [name: string, greeting?: string]Практический пример: Type-safe Decorator
Заголовок раздела «Практический пример: Type-safe Decorator»// Создание type-safe декоратора для функцийtype AnyFunction = (...args: any[]) => any;
function withLogging<T extends AnyFunction>( fn: T): (...args: Parameters<T>) => ReturnType<T> { return (...args: Parameters<T>): ReturnType<T> => { console.log(`Calling ${fn.name} with:`, args); const result = fn(...args); console.log(`Result:`, result); return result; };}
// Использованиеfunction add(a: number, b: number): number { return a + b;}
const loggedAdd = withLogging(add);
// Type-safe вызовconst result = loggedAdd(5, 3); // number, логирует: Calling add with: [5, 3], Result: 8
// Async версияfunction withAsyncLogging<T extends (...args: any[]) => Promise<any>>( fn: T): (...args: Parameters<T>) => ReturnType<T> { return async (...args: Parameters<T>): Promise<Awaited<ReturnType<T>>> => { console.log(`Async calling ${fn.name} with:`, args); const result = await fn(...args); console.log(`Async result:`, result); return result; };}
async function fetchUser(id: string): Promise<User> { // fetch implementation}
const loggedFetchUser = withAsyncLogging(fetchUser);const user = await loggedFetchUser('123'); // UserConstructorParameters<T>
Заголовок раздела «ConstructorParameters<T>»ConstructorParameters<T> извлекает типы параметров конструктора:
// Определение ConstructorParameters (встроенное)type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never;
// Примерclass User { constructor( public name: string, public email: string, public age: number ) {}}
type UserConstructorParams = ConstructorParameters<typeof User>;// [name: string, email: string, age: number]
// Создание фабрикиfunction createInstance<T extends new (...args: any[]) => any>( constructor: T, ...args: ConstructorParameters<T>): InstanceType<T> { return new constructor(...args);}
// UserInstanceType<T>
Заголовок раздела «InstanceType<T>»InstanceType<T> извлекает тип экземпляра из конструктора:
// Определение InstanceType (встроенное)type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;
// Примерclass Product { constructor( public id: string, public name: string, public price: number ) {}
getInfo(): string { return `${this.name} - $${this.price}`; }}
type ProductInstance = InstanceType<typeof Product>;// Product
// Generic фабрика с правильным типом возвратаfunction createFactory<T extends new (...args: any[]) => any>( constructor: T) { return (...args: ConstructorParameters<T>): InstanceType<T> => { return new constructor(...args); };}
const productFactory = createFactory(Product);const product = productFactory('1', 'Laptop', 999); // ProductЖизненный пример: Type-safe Event Handler
Заголовок раздела «Жизненный пример: Type-safe Event Handler»// Система type-safe event handlerstype EventHandler<T extends (...args: any[]) => any> = { handler: T; params: Parameters<T>; result: ReturnType<T>;};
class EventEmitter<TEvents extends Record<string, (...args: any[]) => any>> { private listeners: { [K in keyof TEvents]?: Array<TEvents[K]>; } = {};
on<K extends keyof TEvents>(event: K, handler: TEvents[K]): void { if (!this.listeners[event]) { this.listeners[event] = []; } this.listeners[event]!.push(handler); }
emit<K extends keyof TEvents>( event: K, ...args: Parameters<TEvents[K]> ): ReturnType<TEvents[K]>[] { const handlers = this.listeners[event]; if (!handlers) return [];
return handlers.map(handler => handler(...args)); }
off<K extends keyof TEvents>(event: K, handler: TEvents[K]): void { const handlers = this.listeners[event]; if (handlers) { const index = handlers.indexOf(handler); if (index !== -1) { handlers.splice(index, 1); } } }}
// Определение событийinterface AppEvents { userLogin: (userId: string, timestamp: number) => void; dataLoaded: (data: any[]) => boolean; error: (message: string, code: number) => void;}
const emitter = new EventEmitter<AppEvents>();
// Type-safe подпискаemitter.on('userLogin', (userId, timestamp) => { // userId: string, timestamp: number console.log(`User ${userId} logged in at ${timestamp}`);});
emitter.on('dataLoaded', (data) => { // data: any[] console.log(`Loaded ${data.length} items`); return true; // boolean (ReturnType)});
// Type-safe вызовemitter.emit('userLogin', 'user123', Date.now()); // ✓const results = emitter.emit('dataLoaded', [1, 2, 3]); // boolean[]
// emitter.emit('userLogin', 'user123'); // ✗ Ошибка: отсутствует timestampThisParameterType<T>
Заголовок раздела «ThisParameterType<T>»ThisParameterType<T> извлекает тип this из функции:
// Определение ThisParameterTypetype ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown;
// Примерfunction greet(this: User, greeting: string): string { return `${greeting}, ${this.name}!`;}
type ThisType = ThisParameterType<typeof greet>;// User
// OmitThisParameter - удаление this из сигнатурыtype OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T;
type GreetWithoutThis = OmitThisParameter<typeof greet>;// (greeting: string) => stringКомпозиция Utility Types
Заголовок раздела «Композиция Utility Types»// Создание сложных типов из функцийtype FunctionInfo<T extends (...args: any[]) => any> = { params: Parameters<T>; return: ReturnType<T>; length: Parameters<T>['length'];};
function processOrder( orderId: string, items: string[], total: number): { success: boolean; orderId: string } { return { success: true, orderId };}
type ProcessOrderInfo = FunctionInfo<typeof processOrder>;// {// params: [orderId: string, items: string[], total: number];// return: { success: boolean; orderId: string };// length: 3;// }
// Async function infotype AsyncFunctionInfo<T extends (...args: any[]) => Promise<any>> = { params: Parameters<T>; return: Awaited<ReturnType<T>>; async: true;};
async function loadData(url: string): Promise<{ data: any[] }> { const response = await fetch(url); return response.json();}
type LoadDataInfo = AsyncFunctionInfo<typeof loadData>;// {// params: [url: string];// return: { data: any[] };// async: true;// }Type-safe Function Wrapper
Заголовок раздела «Type-safe Function Wrapper»// Universal function wrapperfunction createWrapper<T extends (...args: any[]) => any>( fn: T, before?: (...args: Parameters<T>) => void, after?: (result: ReturnType<T>) => void): (...args: Parameters<T>) => ReturnType<T> { return (...args: Parameters<T>): ReturnType<T> => { before?.(...args); const result = fn(...args); after?.(result); return result; };}
// Использованиеfunction calculate(a: number, b: number): number { return a + b;}
const wrappedCalculate = createWrapper( calculate, (a, b) => console.log(`Input: ${a}, ${b}`), (result) => console.log(`Output: ${result}`));
wrappedCalculate(5, 3); // Input: 5, 3 / Output: 8
// Async wrapperfunction createAsyncWrapper<T extends (...args: any[]) => Promise<any>>( fn: T, before?: (...args: Parameters<T>) => void, after?: (result: Awaited<ReturnType<T>>) => void): (...args: Parameters<T>) => ReturnType<T> { return async (...args: Parameters<T>): Promise<Awaited<ReturnType<T>>> => { before?.(...args); const result = await fn(...args); after?.(result); return result; };}Ключевые моменты
Заголовок раздела «Ключевые моменты»ReturnType<T>извлекает тип возврата функцииParameters<T>извлекает типы параметров как tupleConstructorParameters<T>извлекает параметры конструктораInstanceType<T>извлекает тип экземпляра классаThisParameterType<T>извлекает типthisиз функции- Используются для создания type-safe декораторов и wrappers
- Критически важны для HOF (higher-order functions)
- Комбинируются для создания сложных типов из функций
- Работают с async функциями (используйте
Awaitedдля unwrap Promise) - Основа для создания type-safe event systems и frameworks