From harness-claude
Validates arrays, tuples, records, maps, and sets using Zod collection primitives for uniform lists, fixed-length data, key-value maps, and cardinality constraints.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Validate arrays, tuples, records, maps, and sets with Zod's collection primitives
Uses Zod schemas for runtime validation of APIs, forms, env vars, and external data while inferring TypeScript types as single source of truth.
Creates reusable Zod v4 schemas to validate API payloads, forms, and config inputs in TypeScript apps. Handles coercion, transforms, errors, and type inference for runtime type safety.
Share bugs, ideas, or general feedback.
Validate arrays, tuples, records, maps, and sets with Zod's collection primitives
z.array() for lists of items with a uniform element schema:import { z } from 'zod';
const TagsSchema = z.array(z.string());
const NumberListSchema = z.array(z.number().positive());
const UserListSchema = z.array(UserSchema);
.min(), .max(), and .length():const AtLeastOneSchema = z.array(z.string()).min(1, 'At least one item required');
const BoundedSchema = z.array(z.number()).min(1).max(10);
const ExactSizeSchema = z.array(z.string()).length(3, 'Must have exactly 3 items');
.nonempty() as a type-aware alternative to .min(1) — the inferred type becomes a non-empty tuple:const NonEmptyTags = z.array(z.string()).nonempty('At least one tag is required');
type NonEmptyTags = z.infer<typeof NonEmptyTags>; // [string, ...string[]]
z.tuple() for fixed-length arrays with positional types:const PointSchema = z.tuple([z.number(), z.number()]);
// [x, y] — exactly two numbers
const RGBSchema = z.tuple([
z.number().min(0).max(255),
z.number().min(0).max(255),
z.number().min(0).max(255),
]);
// Tuple with rest element (fixed prefix + variable suffix)
const AtLeastTwoStrings = z.tuple([z.string(), z.string()]).rest(z.string());
z.record() for key-value maps where keys are strings and values share a type:// Record with string keys and number values
const ScoreMapSchema = z.record(z.number());
// Equivalent to { [key: string]: number }
// Record with validated string keys
const EnvVarsSchema = z.record(z.string().min(1));
// Record with typed keys (uses z.enum or z.string())
const FeatFlagsSchema = z.record(z.enum(['featureA', 'featureB', 'featureC']), z.boolean());
z.map() for JavaScript Map objects:const StringToNumberMap = z.map(z.string(), z.number());
// Parses: new Map([['a', 1], ['b', 2]])
z.set() for JavaScript Set objects:const UniqueTagsSchema = z.set(z.string());
const BoundedSetSchema = z.set(z.number()).min(1).max(5);
// Parses: new Set([1, 2, 3])
.element to access the element schema of an array for composition:const ItemsSchema = z.array(z.object({ id: z.string(), name: z.string() }));
const ItemSchema = ItemsSchema.element; // z.ZodObject<...>
type Item = z.infer<typeof ItemSchema>;
Array vs tuple — when to choose:
| Scenario | Use |
|---|---|
| Variable-length, homogeneous items | z.array() |
| Fixed-length, positional items | z.tuple() |
| Key-value pairs with string keys | z.record() |
| JS Map/Set preservation | z.map() / z.set() |
Unique element validation:
Zod does not have built-in uniqueness checking. Use .refine():
const UniqueStringsSchema = z.array(z.string()).refine((arr) => new Set(arr).size === arr.length, {
message: 'Array must contain unique values',
});
Transforming arrays:
// Sort after parsing
const SortedNumbersSchema = z.array(z.number()).transform((arr) => [...arr].sort((a, b) => a - b));
// Deduplicate
const DeduplicatedSchema = z.array(z.string()).transform((arr) => [...new Set(arr)]);
// Flatten
const FlatTagsSchema = z.array(z.array(z.string())).transform((arr) => arr.flat());
Paginated response pattern:
function paginatedSchema<T extends z.ZodTypeAny>(itemSchema: T) {
return z.object({
data: z.array(itemSchema),
total: z.number().int().nonnegative(),
page: z.number().int().positive(),
pageSize: z.number().int().positive(),
});
}
const PaginatedUsersSchema = paginatedSchema(UserSchema);
type PaginatedUsers = z.infer<typeof PaginatedUsersSchema>;