Capítulo 34 de 61

Chapter 34: Basic Usage — Define, Parse, Handle Errors, Infer

Core Idea

The standard Zod workflow is: define a schema, call .parse()/.safeParse() on untrusted input, and extract the schema's inferred type with z.infer<> for use throughout your code.

Key Concepts

  • .parse(input): validates input; on success returns a strongly-typed deep clone of the input; on failure throws a ZodError. If the schema contains async refinements/transforms, use .parseAsync() instead — sync .parse() will throw.
  • ZodError.issues: an array of granular issue objects (code, path, message, expected, etc.) describing every validation failure, not just the first one.
  • .safeParse(input): avoids try/catch — returns a discriminated union result: { success: true, data } or { success: false, error }. .safeParseAsync() is the async counterpart.
  • z.infer<typeof schema>: extracts the schema's static TypeScript type.
  • z.input<typeof schema> / z.output<typeof schema>: when a schema transforms its input (e.g. via .transform()), input and output types diverge — extract them independently rather than assuming z.infer covers both.

Code Examples

import * as z from "zod";

const Player = z.object({ username: z.string(), xp: z.number() });

Player.parse({ username: "billie", xp: 100 });
// => { username: "billie", xp: 100 }

const result = Player.safeParse({ username: 42, xp: "100" });
if (!result.success) {
  result.error; // ZodError instance, .issues has one entry per field
} else {
  result.data; // { username: string; xp: number }
}

type Player = z.infer<typeof Player>;

const mySchema = z.string().transform((val) => val.length);
type MySchemaIn = z.input<typeof mySchema>;   // string
type MySchemaOut = z.output<typeof mySchema>; // number
  • What it demonstrates: the full round trip from schema definition to safe parsing to type extraction, including the input/output type split for transforming schemas.

Anti-patterns

  • Using .parse() in a hot path where invalid input is expected and common: the thrown-exception cost and try/catch boilerplate add up — .safeParse()'s result object is usually the better fit for expected failure paths (e.g. form validation).

Key Takeaways

  1. .safeParse() is generally preferable to .parse() + try/catch for user-facing validation where failure is a normal, expected outcome.
  2. ZodError.issues gives every failing field in one pass — don't assume only the first error matters.
  3. Reach for z.input<>/z.output<> (not just z.infer<>) whenever a schema transforms its data, since the pre- and post-parse types genuinely differ.

Connects To

  • Matching an Existing Type: z.toZod<T>() for the reverse direction — starting from a TypeScript type and building a schema that exactly matches it.
  • Formatting Errors: turning ZodError.issues into structured or human-readable output.