Utility type returntype in TypeScript
ReturnType
Theory
TL;DR
- Think of it as a receipt for a function: it tells you what comes out, without hardcoding it.
- Works on callable types only. Pass a function type
T, get backRwhereTis(...args: any[]) => R. - If the function changes its return type, every type built with ReturnType updates too.
- For async functions, ReturnType gives
Promise<T>. UseAwaited<ReturnType<...>>to unwrap it. - On overloaded functions, TypeScript picks the last overload's return type, not the one matching your call.
Quick example
function createUser(name: string, age: number) {
return { name, age, createdAt: new Date() };
}
// No need to manually write { name: string; age: number; createdAt: Date }
type User = ReturnType<typeof createUser>;
// { name: string; age: number; createdAt: Date }
// User stays in sync if createUser changes its return shape
function processUser(user: User) {
console.log(user.name, user.createdAt);
}typeof createUser gives TypeScript the full function type. ReturnType then strips the parameters and keeps only the output.
Key difference
ReturnType extracts only the output type of a function. typeof gives you the whole function signature, including parameters. Parameters<T> does the opposite and extracts the input tuple. ReturnType ignores inputs completely and focuses on what a function delivers.
When to use
- A function's output feeds another function as a parameter: use
ReturnType<typeof producer>to type that parameter. - State type in a React component should match an API fetcher's output:
ReturnType<typeof fetchUser>instead of a separate interface. - You want a type alias for a function's output that updates when the function changes.
- Skip it for simple primitives. If a function returns
string, just writestring.
How the compiler handles this
TypeScript resolves ReturnType<T> using conditional types from TS 2.8. Internally it is defined as T extends (...args: any) => infer R ? R : any. The compiler pattern-matches T against a function signature and captures R from the return position. No runtime cost at all - types are fully erased in JavaScript output.
Common mistakes
Using it on a non-function type:
type Bad = ReturnType<{ name: string }>;
// Error: Type '{ name: string }' does not satisfy the constraint '(...args: any) => any'Fix: use typeof obj.method for object methods.
Forgetting async wraps everything in Promise:
async function fetchData() {
return { id: 1, name: "Alice" };
}
type Wrong = ReturnType<typeof fetchData>; // Promise<{ id: number; name: string }>
type Right = Awaited<ReturnType<typeof fetchData>>; // { id: number; name: string }Awaited<T> was added in TypeScript 4.5.
Overloaded functions always resolve to the last overload:
function parse(input: string): string;
function parse(input: number): number;
function parse(input: any): any { return input; }
type Result = ReturnType<typeof parse>; // number, not string or a unionTypeScript resolves to the last declared overload, not the one you would actually call. This trips up a lot of developers.
Void is a valid ReturnType result:
const log = () => { console.log("hi"); };
type LogReturn = ReturnType<typeof log>; // void
const val: LogReturn = 123; // Errorvoid here means "don't use the return value," not "no type."
Real-world usage
In most codebases, ReturnType shows up most at the API boundary, where fetcher functions hand their results to state or UI components.
- React Query / TanStack Query:
type QueryData = ReturnType<typeof api.fetchPost>to typeuseQuerydata. - Zod:
type Schema = ReturnType<typeof schema.parse>for the validated output type. - Express middleware:
type HandlerReturn = ReturnType<typeof authMiddleware>when chaining handlers.
Follow-up questions
Q: What does ReturnType return for an async function?
A: It returns Promise<T>, not T directly. Chain it with Awaited<ReturnType<typeof fn>> to get the unwrapped type.
Q: How does ReturnType handle overloaded functions?
A: It picks the return type of the last overload, regardless of which overload you would actually call. This is a known TypeScript behavior, not a bug.
Q: What is the difference between ReturnType and Parameters?
A: ReturnType extracts the output type. Parameters extracts the input types as a tuple. They are complementary.
Q: Can you implement ReturnType yourself?
A: Yes: type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : any;. The infer R keyword tells TypeScript to capture whatever sits in the return position into R.
Q: Does it work on class methods?
A: Yes. Use ReturnType<typeof instance.method> or ReturnType<ClassName['methodName']>.
Examples
Basic: syncing a state type with a fetcher
async function fetchUser(id: string) {
const res = await fetch(`/api/users/${id}`);
return res.json() as { id: string; name: string; email: string };
}
// Tracks fetchUser's output automatically
type UserState = Awaited<ReturnType<typeof fetchUser>>;
// { id: string; name: string; email: string }
// Add 'role: string' to fetchUser's return - UserState picks it up with no extra workWriting the type manually as { id: string; name: string; email: string } means updating two places when fetchUser changes. With ReturnType, the type lives in one place.
Intermediate: a function that consumes another function's output
function buildReport(name: string, score: number) {
return {
title: `Report for ${name}`,
score,
passed: score >= 60,
generatedAt: new Date(),
};
}
// processReport is always compatible with buildReport's output shape
function processReport(report: ReturnType<typeof buildReport>) {
if (report.passed) {
console.log(`${report.title} - score: ${report.score}`);
}
}
processReport(buildReport("Alice", 85)); // OKThis pattern is common in data processing layers where one function hands off to another. Change buildReport, and TypeScript flags any mismatch in processReport at compile time.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.