Question

Get Function Argument Types with Parameters in TypeScript

typescripttypescript-conditional-types

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>.

ReturnType follows the same rule: ReturnType<typeof test>.

Mental Model

Think of a function as a machine with an input tray and an output slot.

  • typeof test is the machine's instruction sheet: it says the machine accepts a string and a number.
  • 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 test asks for named properties on the machine object, such as .name or custom attached properties. It does not ask what belongs in the input tray.
Take Quiz

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:

  1. typeof formatPrice produces the function type:

    (amount: number, currency: string) => string
    
  2. Parameters<typeof formatPrice> inspects the function's parameter list.

  3. It creates this tuple type:

    [amount: number, currency: string]
    
  4. args must match the tuple's length, order, and types. [19.5, "USD"] is valid.

  5. The spread syntax, ...args, expands the tuple into two function arguments:

    formatPrice(19.5, "USD")
    
  6. result has type string, inferred from formatPrice'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
Take Quiz

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 techniqueWhat it extracts or doesExample result
Parameters<F>Parameter tuple from a function type[string, number]
ReturnType<F>Return type from a function typestring
ConstructorParameters<C>Constructor parameter tuple[name: string]
typeof valueType of a runtime value(a: string) => void
keyof TProperty names of an object type"id" | "name"
F extends (...args: infer P) => any ? P : neverManual 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.

Take Quiz

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> requires T to be a callable function type.
  • The result is a tuple, preserving parameter order, optional parameters, and rest parameters.
  • Use typeof when the function is declared as a value.
  • keyof accesses 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.

Take Quiz

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.

Take Quiz

Keep learning

Related questions

View more