TypeScript: Passing Some Optional Parameters While Skipping Others

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

The Curse of Skipped Parameters in TypeScript

\n

Have you ever written a function with ten optional parameters, and then tried to pass only the fifth and tenth? And immediately got a compilation error, or worse, a silent logical bug? Sound familiar?

\n

On Stack Overflow, this question has gathered thousands of views. TypeScript developers constantly stumble over the same rake: how to pass one optional parameter while skipping another? After all, the standard syntax doesn't allow this – parameters are passed by position, not by name.

\n

Today, we will finally sort out this pain once and for all. I will show you 4 working ways to solve the problem: from simple refactoring to advanced techniques with partial objects. Let's go!

\n\n

Why is this even a problem? The Positional Curse

\n

In TypeScript (as in JavaScript), function parameters are passed in order. If you have a function:

\n
function createUser(\n  name: string,\n  age?: number,\n  email?: string,\n  isAdmin?: boolean\n) { /* ... */ }
\n

You cannot call it like this: createUser('John', , 'john@mail.com'). Skipping a parameter leaves a hole that TypeScript won't digest. You'll have to pass undefined or null, which clutters the code and logic.

\n

But there is good news: we can bypass this limitation in several elegant ways. Let's break down each one.

\n\n

Method 1: Parameter Object (The Most Correct)

\n

This is the recommended pattern in serious projects. Instead of a bunch of positional parameters, we use a single object with optional fields:

\n
interface UserOptions {\n  name: string;\n  age?: number;\n  email?: string;\n  isAdmin?: boolean;\n}\n\nfunction createUser(options: UserOptions): void {\n  const { name, age, email, isAdmin } = options;\n  console.log(`User ${name} created, age: ${age ?? 'not specified'}`);\n}\n\n// Now you can pass only what's needed!\ncreateUser({ name: 'Anna', email: 'anna@mail.com' });\ncreateUser({ name: 'Peter', isAdmin: true });\n
\n

The advantages are obvious:

\n
    \n
  • No matter how many parameters – we pass only what's needed
  • \n
  • The code is self-documenting: it's immediately clear what each field is for
  • \n
  • Easy to add new fields without changing the signature
  • \n
\n

This approach solves 90% of problems with optional parameters. But if you're working with legacy code or cannot change the API – read on.

\n\n

Method 2: Function Overloading

\n

TypeScript allows you to declare multiple signatures for one function. This is convenient when you have specific combinations of parameters:

\n
// Declare overloads\nfunction getData(url: string): Promise<any>;\nfunction getData(url: string, timeout: number): Promise<any>;\nfunction getData(url: string, timeout: number, retries: number): Promise<any>;\n\n// Implementation (must be the most general)\nfunction getData(\n  url: string,\n  timeout?: number,\n  retries?: number\n): Promise<any> {\n  // logic\n  return fetch(url, { signal: AbortSignal.timeout(timeout ?? 5000) });\n}\n\n// Now you can call it like this:\ngetData('/api/users');               // timeout = 5000 (default)\ngetData('/api/users', 3000);         // timeout = 3000\n// But like this – you cannot (skipping timeout is not allowed without overloading):\n// getData('/api/users', undefined, 2); // bad\n
\n

Downside: overloads don't solve the problem of skipping middle parameters. They only provide strict combinations. For flexible passing, it's better to use the first method.

\n\n

Method 3: Rest Parameters + Destructuring (Exotic)

\n

If you want to preserve positionality but add flexibility, you can use tuples with rest:

\n
type OptionalParams = [number, string?, boolean?];\n\nfunction processData(\n  required: string,\n  ...opt                

Blogs

Book Recommendations