Skip to main content

Basic Usage

Create a schema for JavaScript Set objects with validated unique values.
import { z } from 'zod';

const StringSet = z.set(z.string());

const validSet = new Set(['alice', 'bob', 'charlie']);
StringSet.parse(validSet); // ✓ Valid

const invalidSet = new Set(['alice', 123]);
StringSet.parse(invalidSet); // ✗ Invalid - values must be strings

type StringSet = z.infer<typeof StringSet>;
// Set<string>

Signature

function set<Value extends SomeType>(
  valueType: Value,
  params?: string | ZodSetParams
): ZodSet<Value>
valueType
ZodType
required
Schema to validate all set values.
params
string | ZodSetParams
Optional error message (string) or configuration object.

Uniqueness

Sets automatically ensure uniqueness:
const numbers = new Set([1, 2, 3, 2, 1]);
console.log(numbers.size); // 3 - duplicates removed
console.log([...numbers]); // [1, 2, 3]

const NumberSet = z.set(z.number());
NumberSet.parse(numbers); // ✓ Valid - Set contains [1, 2, 3]
Sets use value equality for primitives (string, number, boolean) and reference equality for objects. Two objects with identical properties are considered different values.
const objects = new Set([
  { id: 1, name: 'Alice' },
  { id: 1, name: 'Alice' }, // Different reference - both kept!
]);
console.log(objects.size); // 2 - objects are different references

Methods

min()

Set a minimum size for the set.
const AtLeastThree = z.set(z.string()).min(3);

const valid = new Set(['a', 'b', 'c']);
AtLeastThree.parse(valid); // ✓ Valid

const invalid = new Set(['a', 'b']);
AtLeastThree.parse(invalid); // ✗ Invalid - too few values
Signature:
min(minSize: number, params?: string | ZodCheckMinSizeParams): this
minSize
number
required
Minimum number of unique values required.
params
string | object
Optional error message or configuration object.
z.set(z.string()).min(3, 'Need at least 3 unique values')

max()

Set a maximum size for the set.
const AtMostFive = z.set(z.number()).max(5);

const valid = new Set([1, 2, 3, 4, 5]);
AtMostFive.parse(valid); // ✓ Valid

const invalid = new Set([1, 2, 3, 4, 5, 6]);
AtMostFive.parse(invalid); // ✗ Invalid - too many values
Signature:
max(maxSize: number, params?: string | ZodCheckMaxSizeParams): this
maxSize
number
required
Maximum number of unique values allowed.
params
string | object
Optional error message or configuration object.
z.set(z.number()).max(5, 'Cannot exceed 5 unique values')

size()

Set an exact size for the set.
const ExactlyThree = z.set(z.boolean()).size(2);

const valid = new Set([true, false]);
ExactlyThree.parse(valid); // ✓ Valid (booleans can only have 2 unique values)

const invalid = new Set([true]);
ExactlyThree.parse(invalid); // ✗ Invalid - wrong size
Signature:
size(size: number, params?: string | ZodCheckSizeEqualsParams): this
size
number
required
Exact number of unique values required.
params
string | object
Optional error message or configuration object.

nonempty()

Require at least one value (equivalent to .min(1)).
const NonEmptySet = z.set(z.string()).nonempty();

const valid = new Set(['item']);
NonEmptySet.parse(valid); // ✓ Valid

const invalid = new Set();
NonEmptySet.parse(invalid); // ✗ Invalid - empty set
Signature:
nonempty(params?: string | ZodCheckMinSizeParams): this
params
string | object
Optional error message or configuration object.

Complex Value Types

Object Values

const UserSet = z.set(
  z.object({
    id: z.string(),
    name: z.string(),
  })
);

type UserSet = z.infer<typeof UserSet>;
// Set<{ id: string; name: string }>

// Note: Each object is a unique reference
const users = new Set();
const alice = { id: '1', name: 'Alice' };
const bob = { id: '2', name: 'Bob' };
users.add(alice);
users.add(bob);
users.add(alice); // Same reference - not added again

console.log(users.size); // 2
UserSet.parse(users); // ✓ Valid

Enum Values

const Status = z.enum(['pending', 'approved', 'rejected']);
const StatusSet = z.set(Status);

type StatusSet = z.infer<typeof StatusSet>;
// Set<"pending" | "approved" | "rejected">

const statuses = new Set(['pending', 'approved']);
StatusSet.parse(statuses); // ✓ Valid

Nested Sets

const NestedSet = z.set(z.set(z.number()));

type NestedSet = z.infer<typeof NestedSet>;
// Set<Set<number>>

const nested = new Set();
const set1 = new Set([1, 2, 3]);
const set2 = new Set([4, 5, 6]);
nested.add(set1);
nested.add(set2);

NestedSet.parse(nested); // ✓ Valid

Type Inference

const schema = z.set(z.string().email());

type Output = z.infer<typeof schema>;
// Set<string>

type Input = z.input<typeof schema>;
// Set<string>

Common Patterns

Tags/Categories

const Tags = z.set(z.string().min(1).max(20));

type Tags = z.infer<typeof Tags>;
// Set<string>

const articleTags = new Set(['javascript', 'typescript', 'zod']);
Tags.parse(articleTags); // ✓ Valid

Unique IDs

const UserIds = z.set(z.string().uuid()).min(1);

type UserIds = z.infer<typeof UserIds>;
// Set<string>

const ids = new Set([
  '550e8400-e29b-41d4-a716-446655440000',
  'f47ac10b-58cc-4372-a567-0e02b2c3d479',
]);

UserIds.parse(ids); // ✓ Valid

Permissions

const Permission = z.enum(['read', 'write', 'delete', 'admin']);
const Permissions = z.set(Permission).min(1);

type Permissions = z.infer<typeof Permissions>;
// Set<"read" | "write" | "delete" | "admin">

const userPermissions = new Set(['read', 'write']);
Permissions.parse(userPermissions); // ✓ Valid

Graph Nodes

const Node = z.object({
  id: z.string(),
  neighbors: z.set(z.string()), // Set of neighbor IDs
});

type Node = z.infer<typeof Node>;
// { id: string; neighbors: Set<string> }

const node = {
  id: 'A',
  neighbors: new Set(['B', 'C', 'D']),
};

Node.parse(node); // ✓ Valid

Refinements

Add custom validation:
const NoOverlap = z.object({
  allowed: z.set(z.string()),
  blocked: z.set(z.string()),
}).refine(
  (data) => {
    const overlap = new Set(
      [...data.allowed].filter(x => data.blocked.has(x))
    );
    return overlap.size === 0;
  },
  'Allowed and blocked sets cannot overlap'
);

const valid = {
  allowed: new Set(['read', 'write']),
  blocked: new Set(['delete', 'admin']),
};
NoOverlap.parse(valid); // ✓ Valid

const invalid = {
  allowed: new Set(['read', 'write']),
  blocked: new Set(['write', 'delete']), // 'write' in both
};
NoOverlap.parse(invalid); // ✗ Invalid - overlap detected

Set vs Array

FeatureSetArray
UniquenessAutomaticManual
OrderInsertion order (ES6+)Index-based
DuplicatesNot allowedAllowed
LookupO(1) averageO(n)
Use CaseUnique valuesOrdered collection
// Set - automatic uniqueness
const setSchema = z.set(z.string());
const set = new Set(['a', 'b', 'a']); // Automatically becomes ['a', 'b']
setSchema.parse(set); // ✓ Valid

// Array - manual uniqueness
const arraySchema = z.array(z.string());
const array = ['a', 'b', 'a']; // Duplicates preserved
arraySchema.parse(array); // ✓ Valid - [a, b, a]

// Array with uniqueness validation
const uniqueArraySchema = z.array(z.string())
  .refine(
    arr => new Set(arr).size === arr.length,
    'Array must contain unique values'
  );

uniqueArraySchema.parse(['a', 'b', 'a']); // ✗ Invalid - duplicates

Transformations

Transform set data:
const SetToArray = z.set(z.string())
  .transform((set) => Array.from(set).sort());

const input = new Set(['charlie', 'alice', 'bob']);
const result = SetToArray.parse(input);
// result: ['alice', 'bob', 'charlie']

type Result = z.infer<typeof SetToArray>;
// string[]
const ArrayToSet = z.array(z.number())
  .transform((arr) => new Set(arr));

const input = [1, 2, 3, 2, 1]; // Contains duplicates
const result = ArrayToSet.parse(input);
// result: Set { 1, 2, 3 }

type Result = z.infer<typeof ArrayToSet>;
// Set<number>
  • z.array() - For ordered collections with duplicates
  • z.map() - For key-value pairs
  • z.enum() - For defining value options

Build docs developers (and LLMs) love