From harness-claude
Defines runtime-validated TypeScript schemas using Zod's z.object, primitives, enums, literals, composition, and parsing for API responses, forms, env vars, query params.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Define runtime-validated TypeScript schemas with z.object, primitives, enums, literals, and schema composition
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.
Define runtime-validated TypeScript schemas with z.object, primitives, enums, literals, and schema composition
npm install zodz namespace: import { z } from 'zod'z.string(), z.number(), z.boolean(), z.date(), z.bigint(), z.symbol()z.undefined(), z.null(), z.void(), z.any(), z.unknown(), z.never()z.object() to compose fields into a schema object — each key maps to a Zod type:import { z } from 'zod';
const UserSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1),
email: z.string().email(),
age: z.number().int().positive().optional(),
role: z.enum(['admin', 'editor', 'viewer']),
createdAt: z.date(),
});
z.enum() for a fixed set of string literals — prefer over z.union([z.literal(...)]) for simple cases:const StatusSchema = z.enum(['active', 'inactive', 'pending']);
type Status = z.infer<typeof StatusSchema>; // 'active' | 'inactive' | 'pending'
z.literal() for exact value matching:const TrueSchema = z.literal(true);
const FortyTwoSchema = z.literal(42);
const HelloSchema = z.literal('hello');
const ProfileSchema = z.object({
bio: z.string().optional(), // string | undefined
avatar: z.string().nullable(), // string | null
website: z.string().nullish(), // string | null | undefined
});
const AddressSchema = z.object({
street: z.string(),
city: z.string(),
zip: z.string().regex(/^\d{5}$/),
});
const OrderSchema = z.object({
id: z.string().uuid(),
shippingAddress: AddressSchema,
billingAddress: AddressSchema.optional(),
});
.parse() to throw on failure, .safeParse() to handle errors gracefully:// Throws ZodError if invalid
const user = UserSchema.parse(rawData);
// Returns { success: true, data } or { success: false, error }
const result = UserSchema.safeParse(rawData);
if (result.success) {
console.log(result.data); // fully typed
} else {
console.error(result.error.issues);
}
export const UserSchema = z.object({ ... })
export type User = z.infer<typeof UserSchema>
Zod schemas are composable objects — they are not just validators, they are type constructors. Every schema carries both a runtime validator and a TypeScript type. The z.infer<typeof Schema> pattern eliminates the dual-maintenance problem of keeping an interface and a validator in sync.
Schema composition strategies:
z.object(). Good for simple models..extend() to add fields without re-defining the base. See zod-object-patterns.Primitive coercion:
Zod does not coerce by default. z.number().parse("42") throws. Use z.coerce.number() when input may be a string (e.g., URL query params, form fields):
const PageSchema = z.object({
page: z.coerce.number().int().positive().default(1),
limit: z.coerce.number().int().min(1).max(100).default(20),
});
Default values:
const ConfigSchema = z.object({
timeout: z.number().default(5000),
retries: z.number().default(3),
debug: z.boolean().default(false),
});
Recursive schemas:
For tree structures, use z.lazy():
type Category = {
name: string;
subcategories: Category[];
};
const CategorySchema: z.ZodType<Category> = z.lazy(() =>
z.object({
name: z.string(),
subcategories: z.array(CategorySchema),
})
);
When NOT to use Zod schema definition directly:
z.discriminatedUnion() (see zod-union-discriminated)zod-string-validation)zod-object-patterns)