Skip to content

feat: add .invert() method to ZodCodec - #5770

Merged
colinhacks merged 2 commits into
colinhacks:mainfrom
mahmoodhamdi:feat/codec-invert
Apr 28, 2026
Merged

feat: add .invert() method to ZodCodec#5770
colinhacks merged 2 commits into
colinhacks:mainfrom
mahmoodhamdi:feat/codec-invert

Conversation

@mahmoodhamdi

Copy link
Copy Markdown
Contributor

Summary

Closes #5625

Adds an .invert() method to ZodCodec (and ZodMiniCodec) that returns a new codec with swapped input/output schemas and swapped decode/encode transforms.

Usage

const isoDateCodec = z.codec(z.iso.datetime(), z.date(), {
  decode: (isoString) => new Date(isoString),
  encode: (date) => date.toISOString(),
});

// Inverted: Date → ISO string
const dateToIso = isoDateCodec.invert();

z.decode(dateToIso, new Date("2024-01-15T10:30:00.000Z"));
// → "2024-01-15T10:30:00.000Z"

z.encode(dateToIso, "2024-01-15T10:30:00.000Z");
// → Date object

This is useful when composing schemas in pipelines where the direction needs to be flipped — for example, form validation schemas in react-hook-form where the input type is string but the output should be a parsed value.

Implementation

The method creates a new codec with:

  • def.indef.out swapped
  • def.transformdef.reverseTransform swapped

Zero runtime overhead — just a new instance with references swapped.

Files changed

  • packages/zod/src/v4/classic/schemas.tsZodCodec interface + constructor
  • packages/zod/src/v4/mini/schemas.tsZodMiniCodec interface + constructor
  • packages/zod/src/v4/classic/tests/codec.test.ts — 5 new test cases

Test plan

  • Basic invert: decode/encode directions swapped correctly
  • Round trip: invert preserves data through decode→encode cycle
  • Type inference: z.input/z.output types correctly swapped
  • Double invert: .invert().invert() behaves like original
  • Invert with pipe: works with piped schemas
  • Full test suite passes (3585 tests, 0 type errors)
  • Format and lint checks pass

@colinhacks

Copy link
Copy Markdown
Owner

Interesting idea, but I don't think this should be a method. I'd be open to a top level z.invertCodec() utility function though!

@colinhacks colinhacks closed this Apr 28, 2026
@colinhacks colinhacks reopened this Apr 28, 2026
mahmoodhamdi and others added 2 commits April 27, 2026 18:57
Adds an .invert() method that returns a new codec with swapped input/output
schemas and swapped decode/encode transforms. This enables composing codecs
in either direction without manually re-declaring them.

  const dateToIso = isoDateCodec.invert();
  z.decode(dateToIso, new Date()); // → ISO string

Closes colinhacks#5625
Drop the codec instance method in favor of a top-level utility function.
New methods on schemas raise the API surface; a free function keeps the
codec interface lean and is more tree-shakable.
@colinhacks

Copy link
Copy Markdown
Owner

Pushed a follow-up that drops .invert() and replaces it with a top-level z.invertCodec(codec) utility (mirrored in zod/mini). Also rebased onto main. Going to merge once CI is green.

const dateToIso = z.invertCodec(isoDateCodec);
z.decode(dateToIso, new Date()); // → ISO string

@pullfrog

pullfrog Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

TL;DR — Adds a z.invertCodec() utility function that returns a new codec with swapped input/output schemas and swapped decode/encode transforms, closing #5625.

Key changes

  • Add invertCodec utility to classic and mini APIs — Creates a new ZodCodec (or ZodMiniCodec) with def.indef.out and def.transformdef.reverseTransform swapped. Zero runtime overhead — just a new instance with references exchanged.
  • Add tests for invertCodec — Covers basic inversion, round-trip fidelity, type inference (z.input/z.output correctly swapped), double-invert identity, and a custom codec example.

Summary | 4 files | 2 commits | base: mainfeat/codec-invert


Codec inversion via z.invertCodec()

Before: No way to flip a codec's direction — users had to manually construct a second codec with swapped schemas and transforms.
After: z.invertCodec(codec) returns a new codec where decode becomes encode and vice versa, with input/output types correctly swapped.

The implementation is a standalone function (not a method on ZodCodec) that reads the codec's internal def, swaps inout and transformreverseTransform, and returns a fresh instance. This keeps the codec class unchanged and avoids adding surface area to the prototype.

Why a standalone function instead of a method? The PR was refactored from an initial .invert() method to a top-level z.invertCodec() utility, matching the existing pattern of codec-related functions (like z.codec() itself) living as standalone exports rather than instance methods.

Tests verify that z.invertCodec(z.invertCodec(c)) behaves identically to the original codec, confirming the operation is its own inverse.

classic/schemas.ts · classic/tests/codec.test.ts · mini/schemas.ts · mini/tests/codec.test.ts

Pullfrog  | View workflow run | via Pullfrog | Using Claude Opus𝕏

@colinhacks
colinhacks merged commit 7163e6f into colinhacks:main Apr 28, 2026
6 checks passed
@colinhacks

Copy link
Copy Markdown
Owner

Merged — landed as a top-level z.invertCodec(codec) utility (mirrored in zod/mini) rather than a .invert() method on ZodCodec, in line with the project's preference for top-level utilities over methods on schema instances. Closes #5625. Thanks for putting this together!

Note: this comment was produced by an AI coding assistant.

@colinhacks

Copy link
Copy Markdown
Owner

Landed in Zod 4.4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: add .invert() method to codecs

2 participants