Composition and Transforms
Composition and Transforms
Section titled “Composition and Transforms”Every schema derives from ZodType<TOutput, TInput>, so the composition methods below are available on every schema (guarded so they only apply when input equals output). Each method returns a new wrapping schema; the original is unchanged.
Transform
Section titled “Transform”var schema = Z.String().Transform(s => s.ToUpperInvariant());var result = schema.Validate("hello"); // "HELLO"ZodTransform<TInput, TOutput> validates the input schema first, then runs the transform. If the transform throws, the exception is caught into a transform_error ValidationError ("Transform failed: {message}").
Transforms chain:
var schema = Z.String().Transform(s => s.Trim()).Transform(s => s.ToUpperInvariant());ZodString ships convenience transforms: .ToLower(), .ToUpper(), and .Trim().
Refine
Section titled “Refine”ZodRefinement<T> runs the base schema first, then a predicate. A failing predicate produces code refinement_failed (custom message or "Custom validation failed").
var even = Z.Number().Refine(n => n % 2 == 0, "Must be even");SuperRefine
Section titled “SuperRefine”ZodSuperRefinement<T> takes an Action<RefineCtx<T>> and can emit multiple, path-located issues.
var password = Z.String().SuperRefine(ctx =>{ if (!ctx.Value.Any(char.IsUpper)) ctx.AddIssue("Must contain an uppercase letter", new[] { "uppercase" }); if (ctx.Value.Length < 8) ctx.AddIssue("too_short", "Must be at least 8 characters", new[] { "length" });});RefineCtx<T> exposes:
T Value— the value being refined.ImmutableArray<string> Path— the base path.AddIssue(string code, string message, string[]? path)— appends to the base path; throwsArgumentExceptionfor null/whitespace code or message.AddIssue(string message, string[]? path)— shorthand with coderefinement_failed.Issues/HasIssues.
ZodPipe<TSourceOutput, TTargetOutput> runs the source schema, then validates its output against a target schema.
var schema = Z.String().Pipe(Z.String().Min(10));ZodCatch<T> swallows inner failures and returns a fallback as a successful result. The fallback can be a constant or a factory that receives the input and the errors.
var withFallback = Z.String().Catch("n/a");var computed = Z.Number().Catch((value, errors) => 0);Default
Section titled “Default”ZodDefault<T> substitutes a value when input is null and reports IsOptional = true and ProvidesValueOnMissing = true. The default is not re-validated.
var schema = Z.String().Default("unknown");var result = schema.Validate(null); // "unknown"Prefault
Section titled “Prefault”ZodPrefault<T> substitutes a value when the input equals default(T), then still validates the substituted value through the inner schema.
var schema = Z.Number().Prefault(1);And / Or
Section titled “And / Or”.And(other)→ZodIntersection<T>— both must succeed (see Unions and Discriminated Unions)..Or<TOther>(other)→ZodTypedUnion<T, TOther>— either may succeed.
Example: layered validation
Section titled “Example: layered validation”var schema = Z.String() .Min(3) .Transform(s => s.Trim()) .Refine(s => s.StartsWith("PUR-", StringComparison.Ordinal), "Must start with PUR-") .Default("PUR-UNKNOWN");Behavioral differences at a glance
Section titled “Behavioral differences at a glance”| Wrapper | Trigger | Re-validates substituted value |
|---|---|---|
Default(value) |
null input |
No |
Prefault(value) |
input equals default(T) |
Yes |
Catch(value|factory) |
inner schema fails | No (returns fallback as success) |
Refine(predicate) |
predicate returns false | — |
SuperRefine(action) |
issues added to context | — |