Zod integration
This API is available since Optique 0.7.0.
The @optique/zod package provides seamless integration with Zod, enabling you to use Zod schemas for validating command-line arguments. This allows you to leverage Zod's powerful validation capabilities and reuse existing schemas across your CLI and application code.
deno add jsr:@optique/zod zodnpm add @optique/zod zodpnpm add @optique/zod zodyarn add @optique/zod zodbun add @optique/zod zodBasic usage
The zod() function creates a value parser from any Zod schema:
import { zod } from "@optique/zod";
import { z } from "zod";
// Email validation
const email = zod(z.string().email(), { placeholder: "" });
// Port number with range validation
const port = zod(z.coerce.number().int().min(1024).max(65535), { placeholder: 1024 });
// Enum choices
const logLevel = zod(z.enum(["debug", "info", "warn", "error"]), { placeholder: "debug" });String coercion
CLI arguments are always strings, so use z.coerce for non-string types:
// ✅ Correct: Use z.coerce for numbers
const age = zod(z.coerce.number().int().min(0), { placeholder: 0 });
// ❌ Won't work: z.number() expects actual numbers, not strings
const num = zod(z.number(), { placeholder: 0 }); NOTE
Both z.boolean() and z.coerce.boolean() are handled specially: instead of rejecting CLI strings or applying JavaScript truthiness semantics, Optique accepts CLI-friendly literals (true/false, 1/0, yes/no, on/off, case-insensitive).
Transformations
Zod's transformation capabilities work seamlessly with Optique:
// Parse and transform to Date
const startDate = zod(z.string().transform((s) => new Date(s)), { placeholder: new Date(0) });
// Transform to uppercase
const name = zod(z.string().transform((s) => s.toUpperCase()), { placeholder: "" });Custom error messages
Customize error messages for better user experience:
const email = zod(z.string().email(), {
placeholder: "",
metavar: "EMAIL",
errors: {
zodError: (error, input) =>
message`Please provide a valid email address, got ${input}.`
}
});Integration with Optique
Zod parsers work seamlessly with all Optique features:
import { object } from "@optique/core/constructs";
import { option, argument } from "@optique/core/primitives";
import { zod } from "@optique/zod";
import { z } from "zod";
const config = object({
email: option("--email", zod(z.string().email(), { placeholder: "" })),
port: option("-p", "--port", zod(z.coerce.number().int().min(1024).max(65535), { placeholder: 1024 })),
logLevel: option("--log-level", zod(z.enum(["debug", "info", "warn", "error"]), { placeholder: "debug" })),
startDate: argument(zod(z.string().transform((s) => new Date(s)), { placeholder: new Date(0) })),
});Version compatibility
The @optique/zod package supports both Zod v3 (3.25.0+) and Zod v4 (4.0.0+):
- Zod v3: Uses standard error messages from
error.issues[0].message - Zod v4: Automatically uses
prettifyError()when available for better error formatting
Limitations
Async refinements not supported: Since Optique's parsing is synchronous, async Zod features like
refine(async ...)cannot be used. Async boolean schemas are detected when a valid boolean literal is parsed ("true","false", etc.) and throw aTypeError; unrecognized inputs like"maybe"return a normal validation error instead. Perform async validation after parsing if needed.Boolean parsing in unions: The CLI-friendly boolean parsing (accepting
true/false,1/0,yes/no,on/off) applies only when the entire schema is recognized as a boolean type. For unions that are not recognized as wholly boolean, arm precedence is preserved and parsing follows Zod's native union/coercion behavior.
The Zod integration provides a powerful way to reuse validation logic across your entire application while maintaining full type safety and excellent error messages.