๐ก๏ธ Zod Playground โ Mastering Type-safe Validation in TypeScript


Zod is a TypeScript-first schema declaration and validation library. This article demonstrates the usage of Zod for validating and parsing data structures, building form validators, discriminated unions, custom errors, and much more.
๐ฆ Features Covered
โ Schema creation and type inference
โ Safe and unsafe parsing
โ Enums and literals
โ Object extensions and transformations
โ Discriminated unions
โ Record vs Map
โ Default values
โ Optional, nullable, and nullish handling
โ Custom error messages using
errorMap
โ Promise schema
โ Nested schemas and advanced schema composition
โ Clean error reporting using
zod-validation-error
๐ง Basic Schema and Type Inference
const UserSchema = z.object({
username: z.string(),
}).strict();
type userType = z.infer<typeof UserSchema>;
strict()
ensures no extra fields are allowed.z.infer
auto-generates a TypeScript type from the schema.
๐ Parsing Data
1. parse()
โ throws error if invalid
UserSchema.parse({ username: "Kajal" });
2. safeParse()
โ returns success boolean and error
UserSchema.safeParse({ username: "Kajal" });
๐ง Enum & Union
Using native enum:
enum JobPref {
Remote,
Onsite,
Hybrid,
}
z.nativeEnum(JobPref);
Using string literals:
z.enum(["Remote", "Onsite", "Hybrid"]);
๐งฉ Advanced Object Schema
const userSchema2 = z.object({
username: z.string().min(5).max(100),
age: z.number().positive().default(Math.random()),
birthday: z.date().optional(),
isProgrammer: z.boolean().default(true),
website: z.string().url().nullable(),
hobbies: z.array(z.string()).nonempty().min(1).max(4),
workPreference: z.enum(["Remote", "Onsite", "Hybrid"]).optional(),
}).passthrough();
Notes:
.optional()
โ field can be undefined..nullable()
โ field can be null..nonempty()
โ array cannot be empty..default()
โ sets default value if not provided..passthrough()
โ allows extra fields..strict()
โ blocks any extra fields.
๐งช Schema Utilities
pick()
โ include only some fieldsomit()
โ remove specific fieldsdeepPartial()
โ optional deep fieldsextend()
โ add new fieldspartial()
โ all fields optional
๐ Tuple and Set
z.tuple([z.date(), z.number().gt(25)]).rest(z.string()); // fixed and variable parts
z.set(z.number()); // unique array values
๐ Discriminated Union
Perfect for handling polymorphic data:
const textSchema = z.object({ type: z.literal("text"), content: z.string() });
const imageSchema = z.object({ type: z.literal("image"), url: z.string().url(), alt: z.string() });
const messageSchema = z.discriminatedUnion("type", [textSchema, imageSchema]);
This validates objects based on a type
field and automatically picks the right schema.
๐๏ธ Record vs Map
Record (like object with dynamic keys)
z.record(z.number()); // key: string, val: number
z.record(z.string(), z.number()); // key: string, val: number
Map (JS Map object)
z.map(z.string(), z.number()); // key: string, val: number
Use record for plain JS objects and map when working with Map
instances.
๐ Refinement & Custom Errors
const brandEmail = z
.string()
.email()
.refine(val => val.endsWith("kajalpiyaaa.in"), {
message: "Email must end with @kajalpiyaaa.in",
});
Use refine()
to add custom validation logic.
๐งผ Clean Error Output
import { fromZodError } from "zod-validation-error";
const result = brandEmail.safeParse(email);
if (!result.success) {
console.log(fromZodError(result.error));
}
This gives **pretty error messages** instead of long error stacks.
## โ Custom Error Message Globally
```ts
const Schema = z.array(z.string(), {
errorMap: () => ({ message: "yo it's a string!" }),
});
Use errorMap
to define schema-specific error messages.
๐งต Promise Handling
const userAsync = z.promise(z.string());
// Ensures the resolved value is a string
๐ง Best Practices
Use
.infer<typeof Schema>
to keep DRY in types..safeParse()
is great for graceful error handling.Prefer
zod-validation-error
for clean dev experience.Chain validations (like
.min()
,.default()
) as needed.Use discriminated unions for clear type-safe API schemas.
๐ Conclusion
This article showcases most of the core and advanced Zod features, making it an ideal reference for:
Form validation
API validation
Config object schema enforcement
Type-safe client-server communication
Thanks to web dev simplified!
Subscribe to my newsletter
Read articles from Abheeshta P directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Abheeshta P
Abheeshta P
I am a Full-stack dev turning ideas into sleek, functional experiences ๐. I am passionate about AI, intuitive UI/UX, and crafting user-friendly platforms . I am always curious โ from building websites to diving into machine learning and under the hood workings โจ. Next.js, Node.js, MongoDB, and Tailwind are my daily tools. I am here to share dev experiments, lessons learned, and the occasional late-night code breakthroughs. Always evolving, always building.