Skip to Content

Last Updated: 3/12/2026


Tuples

Unlike arrays, tuples are typically fixed-length arrays that specify different schemas for each index.

Basic Tuples

import * as z from "zod"; const MyTuple = z.tuple([ z.string(), z.number(), z.boolean() ]); type MyTuple = z.infer<typeof MyTuple>; // [string, number, boolean] MyTuple.parse(["hello", 42, true]); // ✅ MyTuple.parse(["hello", 42]); // ❌ throws (too few elements) MyTuple.parse(["hello", 42, true, "extra"]); // ❌ throws (too many elements)

Variadic Tuples (Rest Arguments)

To add a variadic (“rest”) argument to a tuple:

const variadicTuple = z.tuple([z.string()], z.number()); // => [string, ...number[]] variadicTuple.parse(["hello"]); // ✅ variadicTuple.parse(["hello", 1, 2, 3]); // ✅ variadicTuple.parse([42]); // ❌ throws (first element must be string)

Type Inference

const PersonTuple = z.tuple([ z.string(), // name z.number(), // age z.boolean().optional() // isActive (optional) ]); type PersonTuple = z.infer<typeof PersonTuple>; // [string, number, boolean?]

Tuples vs Arrays

Tuples

  • Fixed length (or fixed + variadic)
  • Different types for each position
  • Position matters (first element is different from second)
const tuple = z.tuple([z.string(), z.number()]); // [string, number] - exactly 2 elements

Arrays

  • Variable length
  • Same type for all elements
  • Position doesn’t matter
const array = z.array(z.string()); // string[] - any number of strings

Common Use Cases

Function Return Values

// Simulating multiple return values const getUserData = z.tuple([ z.string(), // username z.number(), // id z.date() // lastLogin ]);

Coordinate Systems

const Point2D = z.tuple([z.number(), z.number()]); // [x, y] const Point3D = z.tuple([z.number(), z.number(), z.number()]); // [x, y, z]

CSV Row Parsing

const CsvRow = z.tuple([ z.string(), // name z.string(), // email z.coerce.number(), // age z.enum(["active", "inactive"]) // status ]);

Key-Value Pairs

const KeyValuePair = z.tuple([z.string(), z.unknown()]); // [key, value] const entries = z.array(KeyValuePair); // Array of [string, unknown] pairs

React Hook Returns

// Similar to useState return type const StateHook = z.tuple([ z.string(), // current value z.function() // setter function ]);

Advanced Patterns

Optional Elements

const OptionalTuple = z.tuple([ z.string(), z.number().optional(), z.boolean().optional() ]); OptionalTuple.parse(["hello"]); // ✅ OptionalTuple.parse(["hello", 42]); // ✅ OptionalTuple.parse(["hello", 42, true]); // ✅

Nested Tuples

const NestedTuple = z.tuple([ z.string(), z.tuple([z.number(), z.number()]) // nested tuple ]); type NestedTuple = z.infer<typeof NestedTuple>; // [string, [number, number]]

Combining with Rest Parameters

const MixedTuple = z.tuple( [z.string(), z.number()], // required elements z.boolean() // rest elements ); // [string, number, ...boolean[]] MixedTuple.parse(["hello", 42]); // ✅ MixedTuple.parse(["hello", 42, true, false, true]); // ✅

Best Practices

  1. Use tuples when you know the exact structure and length
  2. Use arrays when all elements have the same type
  3. Add type aliases to make tuple schemas more readable
  4. Document positions with comments for clarity
  5. Consider objects if you need named fields instead of positional access