Skip to main content

Command Palette

Search for a command to run...

Variadic Tuple Types in TypeScript

Updated
2 min read
Variadic Tuple Types in TypeScript
T

Just a guy who loves to write code and watch anime.

Arrays in TypeScript

We want to store information about a user's profile settings:

type ProfileSettings = string[];

const settings: ProfileSettings = [
  "darkMode", // theme
  "compact", // layout
  "12", // fontSize
  "true", // notifications
];

The problem?

This array type is too flexible. Any string can be placed anywhere. We might accidentally switch positions or add wrong values.

// TypeScript is perfectly happy with this
const brokenSettings: ProfileSettings = [
  "12", // fontSize in wrong position
  "darkMode", // theme in wrong position
  "invalid", // random value
  "compact", // layout in wrong position
  "extra", // unexpected extra value
];

We need more precision.

Tuple Types

Enter tuple types:

type ProfileSettings = [
  theme: string,
  layout: string,
  fontSize: string,
  notifications: string
];

Better. Now we have fixed positions. But we're still using strings for everything. We can make it even more precise:

type Theme = "darkMode" | "lightMode";
type Layout = "compact" | "comfortable";
type ProfileSettings = [
  theme: Theme,
  layout: Layout,
  fontSize: number,
  notifications: boolean
];

// Now TypeScript catches our mistakes
const settings: ProfileSettings = ["darkMode", "compact", 16, true];

This works great for fixed structures. But what if we need flexibility?

Variadic Tuple Types

Imagine we want to create a function that adds a timestamp to any settings tuple:

// We want this to work for ANY tuple
function addTimestamp<T extends unknown[]>(settings: T): [...T, number] {
  return [...settings, Date.now()];
}

This is where variadic tuple types come in. They let us work with tuples of any length while preserving their structure:

type WithTimestamp<T extends unknown[]> = [...T, number];

// Works with any tuple while maintaining type safety
type UserSettings = [string, boolean];
type TimestampedSettings = WithTimestamp<UserSettings>; // [string, boolean, number]

type FullSettings = [string, boolean, number, string];
type TimestampedFull = WithTimestamp<FullSettings>; // [string, boolean, number, string, number]

We've gone from using loose array types to precise tuples, and now to flexible, type-safe variadic tuples. Each step makes our code safer while keeping the flexibility we need.

This approach is really useful when creating type-safe tools that need to handle tuples of various lengths and types.