Question
Get Function Argument Types with Parameters in TypeScript
Question
Given this TypeScript function:
function test(a: string, b: number) {
console.log(a);
console.log(b);
}
How can I obtain the parameter types, string and number, as a tuple type? For example, I can use typeof test to get the function type and ReturnType<typeof test> to get its return type. Is there an equivalent utility type for a function's arguments?
Also, why does keyof typeof test evaluate to never, and how does the extends syntax used in conditional-type solutions work?
Short Answer
TypeScript represents a function's parameter list as a tuple type. Use the built-in Parameters<T> utility to extract that tuple from a function type. You will also learn why typeof is needed for function declarations, why keyof is not used for parameters, and how conditional types can implement the same idea.
Concept
A function has a function type describing its inputs and output. For example, this function:
function test(a: string, b: number): void {
console.log(a, b);
}
has a type similar to:
(a: string, b: number) => void
Its input types can be represented as the tuple:
type TestArguments = [a: string, b: number];
TypeScript provides the built-in Parameters<T> utility type to extract this tuple:
type TestArguments = Parameters<typeof test>;
// [a: string, b: number]
This matters when you want one type definition to stay synchronized with a function. If the function's parameters change later, every place that uses Parameters<typeof test> receives the updated tuple automatically.
Parameters<T> expects a function type. test is a runtime value, while typeof test asks TypeScript for the type of that value. Therefore, use Parameters<typeof test>, not Parameters<test>.
ReturnTypefollows the same rule:ReturnType<typeof test>.
Mental Model
Think of a function as a machine with an input tray and an output slot.
typeof testis the machine's instruction sheet: it says the machine accepts astringand anumber.Parameters<typeof test>reads the input tray requirements and returns[string, number].ReturnType<typeof test>reads the output slot requirement and returns the output type.keyof typeof testasks for named properties on the machine object, such as.nameor custom attached properties. It does not ask what belongs in the input tray.
Syntax and Examples
Use Parameters<T> with a function type.
function test(a: string, b: number): void {
console.log(a, b);
}
type TestParameters = Parameters<typeof test>;
// type TestParameters = [a: string, b: number]
const validArguments: TestParameters = ["Ada", 42];
test(...validArguments);
TestParameters is a tuple, not merely (string | number)[]. Tuple positions are preserved:
- the first item must be a
string - the second item must be a
number
const invalidArguments: TestParameters = [42, "Ada"];
// Error: number is not assignable to string
You can also extract one parameter by tuple index:
type FirstParameter = Parameters<typeof test>[0];
// string
type SecondParameter = Parameters<typeof test>[1];
// number
For an existing function type alias, pass the alias directly because it is already a type:
type Formatter = (value: number, currency: string) => string;
type FormatterParameters = Parameters<Formatter>;
// [value: number, currency: string]
Step by Step Execution
Consider a reusable logging wrapper:
function formatPrice(amount: number, currency: string): string {
return `${currency} ${amount.toFixed(2)}`;
}
type PriceArguments = Parameters<typeof formatPrice>;
const args: PriceArguments = [19.5, "USD"];
const result = formatPrice(...args);
Step by step:
-
typeof formatPriceproduces the function type:(amount: number, currency: string) => string -
Parameters<typeof formatPrice>inspects the function's parameter list. -
It creates this tuple type:
[amount: number, currency: string] -
argsmust match the tuple's length, order, and types.[19.5, "USD"]is valid. -
The spread syntax,
...args, expands the tuple into two function arguments:formatPrice(19.5, "USD") -
resulthas typestring, inferred fromformatPrice's return type.
Real World Use Cases
Parameters<T> is useful whenever another API should accept exactly the same arguments as an existing function.
- Logging or timing wrappers: Forward a function's arguments without manually repeating their types.
- Event handlers: Reuse the argument list of a callback or listener.
- Test helpers: Create typed helpers that invoke production functions with valid inputs.
- Dependency injection: Store constructor or factory arguments in a correctly typed tuple.
- API clients: Reuse argument types for retry, caching, or telemetry layers.
Example: a generic function caller:
function callLater<F extends (...args: any[]) => any>(
fn: F,
args: Parameters<F>
): void {
setTimeout(() => fn(...args), 1000);
}
function greet(name: string, excited: boolean): void {
console.log(excited ? `Hello, ${name}!` : `Hello, ${name}.`);
}
callLater(greet, ["Mina", true]);
// callLater(greet, [true, "Mina"]); // Type error
Real Codebase Usage
In real projects, Parameters<T> is commonly paired with generics and argument forwarding.
Typed wrapper functions
A wrapper can preserve the argument list and return type of any callback:
function withLogging<F extends (...args: any[]) => any>(fn: F) {
return (...args: Parameters<F>): ReturnType<F> => {
console.log("Calling with:", args);
return fn(...args);
};
}
function add(left: number, right: number): number {
return left + right;
}
const loggedAdd = withLogging(add);
const total = loggedAdd(2, 3);
loggedAdd is inferred as (left: number, right: number) => number.
Typed constructor arguments
ConstructorParameters<T> is the matching utility for classes:
class User {
constructor(public name: string, public active: boolean) {}
}
type UserArguments = ConstructorParameters<typeof User>;
// [name: string, active: boolean]
const user = new User(...(["Lin", true] as UserArguments));
Validation before forwarding
A wrapper can validate a condition, then pass the original typed arguments onward. This avoids duplicating a function signature in multiple places.
Common Mistakes
Forgetting typeof for a function declaration
test is a value name, not a type name.
function test(a: string, b: number): void {}
type Args = Parameters<test>;
// Error: 'test' refers to a value, but is being used as a type here.
Use:
type Args = Parameters<typeof test>;
Using ReturnType<test> instead of ReturnType<typeof test>
The same value-versus-type rule applies:
type Result = ReturnType<typeof test>;
Using keyof to get arguments
keyof gets property names from object types. Function parameters are not object properties.
function test(a: string, b: number): void {}
type Keys = keyof typeof test;
// usually never
A plain function declaration has no declared custom properties for TypeScript to list, so there are no useful keys. Use Parameters<typeof test> instead.
Replacing a tuple with a union array
This loses argument order and position-specific typing:
type Wrong = (string | number)[];
const values: Wrong = [1, "Ada"]; // Allowed, but wrong order for test
Use the tuple:
type Correct = Parameters<typeof test>;
// [string, number]
Expecting runtime type information
Parameters<T> exists only in TypeScript's type system. It does not generate JavaScript and cannot inspect argument types at runtime. Validate runtime input separately when data comes from users, files, or network requests.
Comparisons
| Tool or technique | What it extracts or does | Example result |
|---|---|---|
Parameters<F> | Parameter tuple from a function type | [string, number] |
ReturnType<F> | Return type from a function type | string |
ConstructorParameters<C> | Constructor parameter tuple | [name: string] |
typeof value | Type of a runtime value | (a: string) => void |
keyof T | Property names of an object type | "id" | "name" |
F extends (...args: infer P) => any ? P : never | Manual conditional-type version of Parameters<F> | [string, number] |
The implementation idea behind Parameters<T> is a conditional type with infer:
type MyParameters<T extends (...args: any[]) => any> =
T extends (...args: infer P) => any ? P : never;
Read extends here as a type-level question: “Does T match a function that accepts some arguments?” If it does, infer P captures those argument types as tuple P.
Cheat Sheet
// Function declaration: use typeof
type Args = Parameters<typeof myFunction>;
type Result = ReturnType<typeof myFunction>;
// Function type alias: no typeof needed
type Handler = (id: string, retry?: boolean) => Promise<void>;
type HandlerArgs = Parameters<Handler>;
// [id: string, retry?: boolean | undefined]
// Individual tuple elements
type First = Parameters<typeof myFunction>[0];
// Forward arguments in a generic wrapper
function wrap<F extends (...args: any[]) => any>(fn: F) {
return (...args: Parameters<F>): ReturnType<F> => fn(...args);
}
Rules:
Parameters<T>requiresTto be a callable function type.- The result is a tuple, preserving parameter order, optional parameters, and rest parameters.
- Use
typeofwhen the function is declared as a value. keyofaccesses property names, not parameter types.- Utility types are erased when TypeScript compiles to JavaScript.
FAQ
How do I get argument types from a function in TypeScript?
Use Parameters<typeof functionName>. For example, Parameters<typeof test> produces [a: string, b: number].
Why do I need typeof in Parameters<typeof test>?
test is a JavaScript value. typeof test converts that value reference into its TypeScript type so a type utility can use it.
Does Parameters<T> include optional parameters?
Yes. For (id: string, verbose?: boolean) => void, it produces [id: string, verbose?: boolean | undefined].
Does Parameters<T> work with rest parameters?
Yes. For (...values: number[]) => number, it produces number[] as the parameter tuple/rest representation.
Why does keyof typeof test return never?
keyof asks for named properties, not function inputs. A normal function declaration has no declared custom properties, so there are no keys to produce.
Can I get parameter names at runtime with Parameters<T>?
No. Parameters<T> is type-only and removed during compilation. JavaScript does not reliably preserve TypeScript parameter types at runtime.
What is the equivalent utility for class constructors?
Use ConstructorParameters<typeof ClassName> to extract constructor arguments.
Can I write Parameters<test>?
No, not for a function declaration. Write Parameters<typeof test>. You can omit typeof only when you already have a function type alias or interface.
Mini Project
Description
Build a typed once wrapper. It wraps a function so it can run only once, while retaining the exact parameter and return types of the original function. This pattern is useful for one-time initialization, lazy setup, and preventing duplicate actions.
Goal
Create a reusable wrapper that accepts only valid arguments for the wrapped function and returns its original result.
Requirements
Define a generic once function that accepts a function.
Use Parameters<F> to preserve the wrapped function's arguments.
Use ReturnType<F> to preserve the wrapped function's result type.
Ensure the wrapped function runs only on its first call.
Demonstrate the wrapper with a function that accepts a string and a number.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.