How do you extract a function's argument types with infer?
TypeScript lets you extract a function's argument types using the infer keyword inside conditional types.
This is one of the most common and useful use cases for infer.
The general idea
We check that some type T is a function, and we tell TypeScript:
"If
Tis a function whose arguments have typeA, infer thatA."
Example 1: Extract the type of one argument
type FirstArg<T> = T extends (arg: infer A) => any ? A : never;
type Example = (x: number) => void;
type Result = FirstArg<Example>; // numberHere:
T extends (arg: infer A) => anychecks whetherTis a function.infer Ais how TypeScript "pulls out" the type of the first argument.- If
Tis not a function, the result isnever.
Example 2: Extract all of a function's arguments as a tuple
type Args<T> = T extends (...args: infer A) => any ? A : never;
type Fn = (x: number, y: string) => boolean;
type Params = Args<Fn>; // [number, string]In this case infer A becomes a tuple of argument types.
Example 3: Extract the return type
type Return<T> = T extends (...args: any[]) => infer R ? R : never;
type Fn = (x: number, y: string) => boolean;
type R = Return<Fn>; // booleanThis is the implementation behind the built-in
ReturnType<T>.
Example 4: Get both the arguments and the result
type FunctionParts<T> = T extends (...args: infer A) => infer R
? { args: A; return: R }
: never;
type Fn = (id: string, active: boolean) => number;
type Parts = FunctionParts<Fn>;
/*
{
args: [string, boolean];
return: number;
}
*/Example 5: The first argument
type FirstArg<T> = T extends (...args: [infer First, ...any[]]) => any
? First
: never;
type Fn = (name: string, age: number) => void;
type A = FirstArg<Fn>; // stringThis uses tuple destructuring:
[infer First, ...any[]].
Example 6: The last argument
type LastArg<T> = T extends (...args: [...any[], infer Last]) => any
? Last
: never;
type Fn = (a: number, b: string, c: boolean) => void;
type L = LastArg<Fn>; // booleanExample 7: The argument types of a class method
class User {
save(id: number, active: boolean) {}
}
type MethodArgs = Args<User["save"]>;
// [number, boolean]Works for any method - just take the type by key
["methodName"]and apply the utility.
Example 8: Implementing an analog of the built-in Parameters<T>
TypeScript already has a ready-made utility type:
type MyParameters<T extends (...args: any[]) => any> =
T extends (...args: infer A) => any ? A : never;
type Fn = (x: number, y: string) => boolean;
type Params = MyParameters<Fn>; // [number, string]This is the equivalent of the standard Parameters<Fn>.
Example 9: Getting the argument type of a callback function
type Callback = (err: Error | null, data: string) => void;
type CallbackArgs = Args<Callback>; // [Error | null, string]Often used when typing APIs, Node.js callbacks, React hooks, and so on.
Example 10: Applying the arguments to another function
type Args<T> = T extends (...args: infer A) => any ? A : never;
function call<T extends (...args: any[]) => any>(fn: T, ...args: Args<T>) {
return fn(...args);
}
function greet(name: string, age: number) {
return `${name}, ${age}`;
}
call(greet, "Tim", 25); // ok
call(greet, 25, "Tim"); // type errorHere Args<T> guarantees the correct order and types of the arguments.
Summary
| What we extract | Example | Result |
|---|---|---|
| One argument | T extends (a: infer A) => any | the type of the first argument |
| All arguments | T extends (...args: infer A) => any | a tuple of arguments |
| Return value | T extends (...args: any[]) => infer R | the result type |
| First argument | T extends (...args: [infer F, ...any[]]) => any | the first argument |
| Last argument | T extends (...args: [...any[], infer L]) => any | the last argument |
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.