Skip to main content

How to Use TypeScript Optional Parameters

TypeScript optional parameters let you call functions with different numbers of arguments, making them flexible and accommodating. This is especially helpful for dealing with various input scenarios without needing to overload functions. In this article, we'll explore practical details and examples of using optional parameters in TypeScript.

Optional parameters are an essential part of the TypeScript type system that help you write more adaptable code. Whether you're building frontend components with React or creating backend functions with Convex, understanding how to properly implement and handle optional parameters will improve your code quality.

Defining Optional Parameters in TypeScript Functions

In TypeScript, you can make a parameter optional by adding a question mark (?) after its name in the function signature. This indicates the parameter doesn't need to be provided when the function is called.

function greet(name: string, greeting?: string) {
console.log(`${greeting || 'Hello'}, ${name}!`);
}

greet("Alice"); // Output: Hello, Alice!
greet("Bob", "Hi"); // Output: Hi, Bob!

The question mark syntax gives you immediate flexibility in how functions can be called, reducing the need for function overloads or extra parameter checking code.

Syntax for Optional Parameters

The syntax for defining optional parameters is simple: just add a ? after the parameter name.

function logMessage(message?: string) {
console.log(message || "No message provided");
}

logMessage(); // Output: No message provided
logMessage("Hello, world!"); // Output: Hello, world!

When you omit an optional parameter, its value becomes undefined. The code above uses the logical OR operator (||) to provide a fallback value when the parameter is undefined.

Using Optional Parameters with Default Values in TypeScript

Optional parameters can also have default values, which are used when the parameter is not provided. This combines the flexibility of optional parameters with guaranteed values:

function log(message: string, level: string = 'info') {
console.log(`[${level}] ${message}`);
}

log("Server started"); // Output: [info] Server started
log("Database connection failed", "error"); // Output: [error] Database connection failed

Default parameters provide a more explicit way to handle missing values compared to the || fallback pattern. They make your code's intent clearer and can help with argument validation when building robust applications.

Deciding Between Optional Parameters and Default Values

When should you use a default value versus a simple optional parameter? Consider these guidelines:

  • Use default values when there's a logical fallback value that makes sense most of the time
  • Use optional parameters without defaults when there's no sensible default, or the behavior should differ significantly when the parameter is absent
  • Consider that parameters with default values don't need the ? syntax, as they're effectively optional already
// Default parameter (no ? needed)
function fetchData(url: string, timeout: number = 5000) {
// Implementation
}

// Optional parameter with no default (needs ?)
function processData(data: any, options?: {sort: boolean, filter: boolean}) {
// Implementation varies significantly based on presence of options
}

When working with default parameter values, TypeScript ensures type safety while providing the flexibility of optional arguments.

Handling Undefined Values with Optional Parameters

Since optional parameters can be undefined, it's important to handle this case properly to avoid runtime errors:

function processUser(user: {name: string, age?: number}) {
console.log(`User: ${user.name}`);

// Safe handling of optional parameter
if (user.age !== undefined) {
console.log(`Age: ${user.age}`);
}
}

Handling Optional Parameters in TypeScript Interfaces

Optional parameters can also be used in interfaces to define properties that might not always be present.

interface ApiResponse {
data: string;
error?: string;
}

There are several strategies for handling undefined optional parameters safely:

  1. Use conditional checks:
if (parameter !== undefined) {
// Use parameter safely
}
  1. Use the logical OR operator for default values:
const value = parameter || defaultValue;
  1. Use nullish coalescing for default values (preserves 0 and ''):
const value = parameter ?? defaultValue;
  1. Use optional chaining with optional parameters:
const result = options?.process?.(data);

When working with Convex functions, handling optional parameters correctly helps you build more flexible APIs without compromising type safety.

Type Guards for Optional Parameters

Type guards are particularly useful when working with optional parameters that could have different types:

function processValue(value?: string | number) {
if (typeof value === 'string') {
return value.toUpperCase();
} else if (typeof value === 'number') {
return value * 2;
} else {
return null; // Handle undefined case
}
}

This pattern works well with union types and ensures your code remains type-safe even with the flexibility of optional parameters.

Checking for the Presence of Optional Parameters

Sometimes you need to know whether an optional parameter was provided, not just handle its potentially undefined value. TypeScript offers several ways to check for parameter presence:

function configureApp(options?: {debug?: boolean, logLevel?: string}) {
// Check if the options object was provided at all
if (options === undefined) {
return defaultConfiguration();
}

// Check if specific options were provided
const debugMode = 'debug' in options ? options.debug : false;
const logLevel = options.logLevel ?? 'info';

// Configure the application
// ...
}

The in operator is useful for checking if a property exists on an object, which differs slightly from checking if its value is undefined. This distinction matters when properties are explicitly set to undefined:

const explicitUndefined = { debug: undefined };
const notProvided = {};

// This is true - property exists but is undefined
'debug' in explicitUndefined;

// This is false - property doesn't exist
'debug' in notProvided;

When building TypeScript applications with Convex, these distinctions help you create flexible APIs that properly handle all edge cases.

Using Object Destructuring with Optional Parameters

Object destructuring provides an elegant way to handle optional parameters, especially when dealing with option objects:

function renderUI({
theme = 'light',
showHeader = true,
user
}: {
theme?: string,
showHeader?: boolean,
user: {name: string, id: string}
}) {
// Implementation using destructured parameters with defaults
}

// Call with only required parameters
renderUI({ user: { name: "Alice", id: "123" } });

// Call with all parameters
renderUI({
theme: 'dark',
showHeader: false,
user: { name: "Bob", id: "456" }
});

This pattern is commonly used in React TypeScript components and other complex interfaces where you need to provide many configuration options.

Managing Multiple Optional Parameters

Functions with multiple optional parameters require special consideration to maintain code clarity and usability:

// Many optional parameters can be unwieldy
function createUser(
name: string,
email?: string,
age?: number,
isAdmin?: boolean,
department?: string,
startDate?: Date
) {
// Implementation
}

// Better approach with options object
function createUser(
name: string,
options?: {
email?: string,
age?: number,
isAdmin?: boolean,
department?: string,
startDate?: Date
}
) {
// Implementation using options.email, options.age, etc.
}

Using an options object has several advantages:

  1. It makes function calls clearer, especially when skipping some optional parameters
  2. It allows for named parameters, reducing errors from parameter order confusion
  3. It makes the function signature more maintainable as requirements change
  4. It works well with interface definitions that can be reused

When building backend functions, this pattern helps you create more maintainable APIs.

Optional Parameter Order Restrictions

In TypeScript, all optional parameters must come after required parameters:

// Correct: required parameters first, then optional
function correct(required1: string, required2: number, optional?: boolean) {}

// Error: optional parameters must come after required parameters
function incorrect(required1: string, optional?: boolean, required2: number) {}

This restriction helps TypeScript maintain type safety when calling functions, as it can't determine which argument should map to which parameter if required parameters follow optional ones. The one exception to this rule is when using default values:

// This works because default parameters are effectively optional
function withDefaults(required: string, withDefault: number = 0, alsoRequired: boolean) {
// Implementation
}

withDefaults("test", 42, true); // All parameters provided
withDefaults("test", undefined, true); // Using default for middle parameter

While this is possible, it can lead to confusing code. For better readability, keep required parameters first, followed by parameters with defaults, and finally truly optional parameters.

Documenting Optional Parameters for Code Clarity

Clear documentation is essential for functions with optional parameters, especially in team environments or public APIs:

/**
* Fetches user data from the API
* @param userId - The unique identifier of the user
* @param options - Optional settings
* @param options.includeActivity - Whether to include user activity data
* @param options.sessionToken - Authentication token (required if user is requesting own data)
* @returns User data object
*/
function fetchUser(
userId: string,
options?: {
includeActivity?: boolean,
sessionToken?: string
}
): Promise<User> {
// Implementation
}

Good documentation clarifies:

  • Which parameters are optional
  • What happens when optional parameters are omitted
  • Any interdependencies between parameters
  • Default values used for optional parameters

When working with TypeScript in larger projects, this documentation becomes increasingly valuable. Tools like TSDoc help generate API documentation automatically from your comments.

Using JSDoc for Function Documentation

JSDoc comments provide a standard way to document TypeScript functions:

/**
* Processes payment for an order
*
* @param orderId - Unique order identifier
* @param amount - Payment amount
* @param {Object} [options] - Optional configuration (square brackets indicate optional)
* @param {string} [options.currency="USD"] - Currency code (defaults to USD)
* @param {boolean} [options.sendReceipt=true] - Whether to send email receipt
* @returns {Promise<PaymentResult>} Result of payment processing
*
* @example
* // Process payment with default options
* processPayment("order-123", 99.99);
*
* // Process payment with custom options
* processPayment("order-456", 149.99, { currency: "EUR", sendReceipt: false });
*/
function processPayment(
orderId: string,
amount: number,
options?: {
currency?: string,
sendReceipt?: boolean
}
): Promise<PaymentResult> {
// Implementation with sensible defaults
const currency = options?.currency || "USD";
const sendReceipt = options?.sendReceipt !== false;

// Process payment
// ...
}

Good documentation helps maintain code quality as your project grows.

Testing Functions with Optional Parameters in TypeScript

Test functions with optional parameters by creating test cases for each parameter combination:

describe('greet function', () => {
it('works with only required parameters', () => {
const result = greet("Alice");
expect(result).toBe("Hello, Alice!");
});

it('works with optional parameters provided', () => {
const result = greet("Bob", "Hi");
expect(result).toBe("Hi, Bob!");
});
});

Include test cases that verify default behaviors when optional parameters are omitted and edge cases with unusual values.

Converting Existing JavaScript Functions to Use TypeScript Optional Parameters

When migrating from JavaScript to TypeScript, identify parameters that aren't always required:

// JavaScript version
function sendNotification(user, message, channel) {
channel = channel || 'email';
// Implementation
}

// TypeScript version
function sendNotification(user: User, message: string, channel?: 'email' | 'sms' | 'push') {
const selectedChannel = channel || 'email';
// Implementation with type safety
}

Look for parameters that use default values, optional chaining, or conditional logic - these are natural candidates for optional parameters in TypeScript.

Final Thoughts on TypeScript Optional Parameters

TypeScript optional parameters make your code more adaptable and maintainable. By marking parameters with the ? symbol, you gain flexibility without sacrificing type safety.

Remember to:

  • Handle undefined values properly
  • Consider using default values when appropriate
  • Group multiple optional parameters into options objects
  • Document your functions clearly

These practices will help you write more flexible TypeScript code that's easier to maintain as your project grows.