# How to Validate Map and Set Collections with Zod: Complete Guide

> Master Zod map and set validation. Learn to validate Map and Set collections with key/value schemas, size constraints, and async refinements for robust data handling. Get the complete guide.

- Repository: [Colin McDonnell/zod](https://github.com/colinhacks/zod)
- Tags: how-to-guide
- Published: 2026-02-23

---

**Zod provides dedicated `z.map()` and `z.set()` schemas that validate JavaScript `Map` and `Set` collections by specifying schemas for keys and values (Map) or values only (Set), with built-in support for size constraints, asynchronous refinements, and immutable read-only outputs.**

The **colinhacks/zod** library (v4) offers first-class schemas for validating JavaScript's built-in collection types. Whether you are enforcing type safety on entries in a `Map<string, number>` or ensuring a `Set` contains unique validated objects, Zod's map and set validation APIs handle runtime type checking, size constraints, and complex async validation workflows.

## Creating Map Schemas in Zod

Use the `z.map()` factory function to create a schema that validates both the keys and values of a JavaScript `Map`. This function requires two arguments: a schema for the key type and a schema for the value type.

### Basic Map Validation

The factory for Map schemas resides in [[`packages/zod/src/v4/mini/schemas.ts`](https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/mini/schemas.ts)](https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/mini/schemas.ts#L1212-L1224). You can define typed Maps for any combination of primitive or complex schemas:

```typescript
import { z } from "zod/v4";

// Validates Map<string, number>
const StringNumberMap = z.map(z.string(), z.number());

// Validates Map<string, { name: string }>
const UserMap = z.map(
  z.string(),
  z.object({ name: z.string() })
);

```

### Size Constraints and Non-Empty Requirements

Both Map and Set schemas expose chainable methods to enforce collection dimensions. These constraints generate issues with codes `too_small` or `too_big` when violated:

```typescript
const SmallMap = StringNumberMap.max(5);    // Maximum 5 entries
const LargeMap = StringNumberMap.min(2);    // Minimum 2 entries
const ExactMap = StringNumberMap.size(3);   // Exactly 3 entries
const NonEmptyMap = StringNumberMap.nonempty(); // At least 1 entry

```

## Parsing and Validating Map Data

The core parsing logic for Maps, including type checking, element validation, and async handling, is implemented in [[`packages/zod/src/v4/core/schemas.ts`](https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/core/schemas.ts)](https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/core/schemas.ts#L2886-L2944).

### Synchronous Parsing

Use `.safeParse()` to validate a Map instance against your schema. The method returns a discriminated union with `success` boolean and either `data` or `error`:

```typescript
const result = StringNumberMap.safeParse(
  new Map([
    ["age", 30],
    ["year", 2024],
  ])
);

if (result.success) {
  // result.data is typed as Map<string, number>
  console.log(result.data);
} else {
  console.error(result.error.format());
}

```

For comprehensive test cases covering parsing behaviors, see [[`packages/zod/src/v4/classic/tests/map.test.ts`](https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/classic/tests/map.test.ts)](https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/classic/tests/map.test.ts).

### Async Validation with Refinements

Map schemas support asynchronous refinements on both keys and values. When parsing with `.safeParseAsync()`, Zod detects promises returned by element schemas and aggregates them using `Promise.all` before finalizing the payload (see the `for …` loop implementation in [[`core/schemas.ts`](https://github.com/colinhacks/zod/blob/main/core/schemas.ts)](https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/core/schemas.ts#L2927-L2944)):

```typescript
const AsyncMap = z.map(
  z.string().refine(async (v) => v.startsWith("id_"), "invalid key"),
  z.number().refine(async (n) => n > 0, "must be positive")
);

await AsyncMap.safeParseAsync(
  new Map([["id_123", 10]]) // ❌ fails both refinements if conditions not met
);

```

## Creating Set Schemas in Zod

Sets are validated using `z.set()`, which accepts a single schema describing the set's element type. The factory is defined in [[`packages/zod/src/v4/mini/schemas.ts`](https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/mini/schemas.ts)](https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/mini/schemas.ts#L1236-L1243).

### Basic Set Validation

```typescript
// Validates Set<boolean>
const BoolSet = z.set(z.boolean());

// Validates Set<string> with email format
const EmailSet = z.set(z.string().email());

```

### Size Constraints

Set schemas support the same size helpers as Maps, issuing `too_small` or `too_big` errors when constraints are violated:

```typescript
const AtLeastTwo = BoolSet.min(2);
const AtMostFive = BoolSet.max(5);
const ExactlyThree = BoolSet.size(3);
const NonEmptySet = BoolSet.nonempty();

```

## Parsing Sets and Handling Validation Errors

The core Set parser, handling iteration, type checks, and error creation, lives in [[`packages/zod/src/v4/core/schemas.ts`](https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/core/schemas.ts)](https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/core/schemas.ts#L2950-L2988).

```typescript
const result = BoolSet.safeParse(new Set([true, false]));

if (result.success) {
  // result.data is Set<boolean>
  console.log([...result.data]); // [true, false]
}

```

Zod emits specific issue codes for common validation failures:

- **`invalid_type`**: The input is not a `Map` or `Set` instance.
- **`invalid_key`**: A Map key fails its schema (e.g., non-primitive object used as key when string expected).
- **`too_small`**: Collection size is below the `.min()` or `.nonempty()` threshold.
- **`too_big`**: Collection size exceeds the `.max()` limit.

## Enforcing Collection Immutability with Readonly

Both `ZodMap` and `ZodSet` schemas expose a `.readonly()` method that returns a schema producing frozen collections. When parsed, the resulting `Map` or `Set` has `Object.isFrozen(value) === true`, guaranteeing immutability after validation. This is implemented via the `$ZodMap` and `$ZodSet` constructors in [[`core/schemas.ts`](https://github.com/colinhacks/zod/blob/main/core/schemas.ts)](https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/core/schemas.ts):

```typescript
const ReadOnlyMap = StringNumberMap.readonly();
const parsed = ReadOnlyMap.parse(new Map([["a", 1]]));

console.log(Object.isFrozen(parsed)); // true
// parsed.set("b", 2); // TypeError: Cannot add property

```

## Summary

- **Map schemas** are created with `z.map(keySchema, valueSchema)` and **Set schemas** with `z.set(elementSchema)`, both defined in [`mini/schemas.ts`](https://github.com/colinhacks/zod/blob/main/mini/schemas.ts).
- **Size constraints** use chainable methods `.min()`, `.max()`, `.size()`, and `.nonempty()` to enforce collection dimensions, generating `too_small` or `too_big` issues.
- **Async validation** is supported via `.safeParseAsync()`, with Zod aggregating promises from element schemas using `Promise.all` before returning results.
- **Read-only collections** enforce runtime immutability through the `.readonly()` method, which freezes the parsed output.
- **Error specificity** includes distinct codes for type mismatches (`invalid_type`), invalid Map keys (`invalid_key`), and size violations.

## Frequently Asked Questions

### How do I validate that a Map contains specific required keys?

Zod's `z.map()` validates the type of keys and values but does not enforce the presence of specific keys by default. To require specific keys like `"id"` or `"name"`, use `.refine()` on the Map schema to check `map.has("id")`, or consider using `z.record()` or `z.object()` if the key structure is fixed and known.

### Can I use objects as keys in a Zod-validated Map?

No. Zod treats non-primitive keys (objects or arrays) as invalid and will emit an issue with `code: "invalid_key"`. The validation logic in [`core/schemas.ts`](https://github.com/colinhacks/zod/blob/main/core/schemas.ts) explicitly checks for primitive key types. Use strings, numbers, or symbols as Map keys when working with Zod schemas.

### What is the difference between `.nonempty()` and `.min(1)` for Sets and Maps?

Functionally, `.nonempty()` and `.min(1)` produce identical validation results for both Map and Set schemas, ensuring the collection contains at least one entry. However, `.nonempty()` provides clearer semantic intent in your schema definitions, indicating that the collection must not be empty rather than merely meeting a numeric minimum.

### How does Zod handle async validation for Maps with multiple entries?

When parsing a Map with asynchronous refinements on keys or values, Zod collects all promises returned during the iteration over Map entries. It then executes `Promise.all` on these promises (as seen in [[`core/schemas.ts`](https://github.com/colinhacks/zod/blob/main/core/schemas.ts)](https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/core/schemas.ts#L2927-L2944)) before finalizing the validation result, ensuring all async checks complete before determining success or aggregating errors.