Как получить тип возвращаемого значения функции в TypeScript?
Решение: использование встроенного типа ReturnType
В TypeScript есть встроенный условный тип ReturnType<T>, который позволяет извлечь тип возвращаемого значения функции. Он работает как с обычными функциями, так и с асинхронными.
Вот как правильно применить его к вашему примеру:
function createUser(name: string, age: number) {
return {
id: Math.random().toString(36),
name: name,
age: age,
createdAt: new Date(),
isActive: true,
permissions: ['read', 'write']
};
}
// Правильный способ получения типа возвращаемого значения:
type User = ReturnType<typeof createUser>;
// Теперь User — это тип объекта, который возвращает функция createUser
const user: User = {
id: 'abc123',
name: 'John',
age: 30,
createdAt: new Date(),
isActive: true,
permissions: ['read', 'write']
};
// Можно использовать в других функциях:
function updateUser(user: User, newName: string): User {
return { ...user, name: newName };
}
Важные нюансы и расширенные примеры
- Обязательно используйте
typeof—ReturnTypeожидает тип функции, а не саму функцию. Поэтому конструкцияReturnType<typeof createUser>— единственно верная. - Работа с асинхронными функциями — для async функций
ReturnTypeвернётPromise<T>. Чтобы получить "чистый" тип, используйтеAwaited:
async function fetchUser(id: string) {
const response = await fetch(`/api/users/${id}`);
return response.json() as Promise<{ name: string; age: number }>;
}
// Тип будет Promise<{ name: string; age: number }>
type AsyncUser = ReturnType<typeof fetchUser>;
// Чтобы получить сам объект, используйте Awaited:
type ActualUser = Awaited<ReturnType<typeof fetchUser>>;
// ActualUser = { name: string; age: number }
- Работа с функциями, имеющими перегрузки —
ReturnTypeвернёт тип последней сигнатуры перегрузки. - Для стрелочных функций и методов класса — работает точно так же:
const getConfig = () => ({ theme: 'dark', lang: 'ru' });
type Config = ReturnType<typeof getConfig>; // { theme: string; lang: string }
class Service {
getData() { return { success: true, data: [] }; }
}
type ServiceData = ReturnType<Service['getData']>; // { success: boolean; data: any[] }
Альтернативный подход: явное объявление типа
Хотя ReturnType — это элегантное решение, иногда лучше явно объявить тип и использовать его как возвращаемый тип функции. Это улучшает читаемость и позволяет документировать структуру данных:
interface User {
id: string;
name: string;
age: number;
createdAt: Date;
isActive: boolean;
permissions: string[];
}
function createUser(name: string, age: number): User {
return {
id: Math.random().toString(36),
name,
age,
createdAt: new Date(),
isActive: true,
permissions: ['read', 'write']
};
}
Когда использовать ReturnType
- Когда функция возвращает сложный или динамический тип, который сложно описать вручную.
- Когда вы работаете с библиотечными функциями, типы которых вы не контролируете.
- Когда нужно быстро получить тип для временного использования.
Совет: Для production-кода предпочитайте явное объявление типов — это делает код самодокументируемым и упрощает рефакторинг. ReturnType отлично подходит для вспомогательных или утилитарных сценариев.
Расширенный подход: создание собственного утилитарного типа
Если вам часто нужно получать типы возвращаемых значений из разных функций, можно создать свой утилитарный тип для удобства:
// Утилита для извлечения типа из функции
type FunctionReturn<T extends (...args: any[]) => any> = ReturnType<T>;
// Использование:
function getConfig() { return { port: 3000, host: 'localhost' }; }
type Config = FunctionReturn<typeof getConfig>;
// Config = { port: number; host: string }
Для асинхронных функций можно создать более специализированную версию:
type AsyncReturn<T extends (...args: any[]) => Promise<any>> = Awaited<ReturnType<T>>;
async function loadData() { return { items: [1, 2, 3] }; }
type Data = AsyncReturn<typeof loadData>;
// Data = { items: number[] }
Ограничения подхода:
ReturnTypeне работает с generic-функциями, если не указать конкретный тип аргументов.- Для функций с перегрузками берётся последняя сигнатура.
Этот подход удобен, когда вы работаете с большим количеством функций и хотите иметь краткий синтаксис для извлечения типов.