๐Ÿ“ฆ colinhacks / tozod

โ˜… 169 stars โ‘‚ 7 forks ๐Ÿ‘ 169 watching โš–๏ธ MIT License
๐Ÿ“ฅ Clone https://github.com/colinhacks/tozod.git
HTTPS git clone https://github.com/colinhacks/tozod.git
SSH git clone git@github.com:colinhacks/tozod.git
CLI gh repo clone colinhacks/tozod
Colin McDonnell Colin McDonnell testing v1 b3bb0cb 5 years ago ๐Ÿ“ History
๐Ÿ“‚ master View all commits โ†’
๐Ÿ“ src
๐Ÿ“„ .eslintignore
๐Ÿ“„ .gitignore
๐Ÿ“„ .prettierrc
๐Ÿ“„ CHANGELOG.md
๐Ÿ“„ CONTRIBUTING.md
๐Ÿ“„ coverage.svg
๐Ÿ“„ ERROR_HANDLING.md
๐Ÿ“„ FUNDING.yml
๐Ÿ“„ jestconfig.json
๐Ÿ“„ LICENSE
๐Ÿ“„ logo.svg
๐Ÿ“„ package.json
๐Ÿ“„ README.md
๐Ÿ“„ tsconfig.json
๐Ÿ“„ tslint.json
๐Ÿ“„ yarn-error.log
๐Ÿ“„ yarn.lock
๐Ÿ“„ README.md

toZod

toZod is a utility for defining Zod schemas that agree with a TypeScript type.

This it the inverse how Zod typically works. By chaining and composing its built-in methods, Zod is able to build up an inferred static type for your schema. This is the opposite: toZod "infers" the structure of a Zod schema from a TS type.

Installation

yarn add tozod

โš  Requires TypeScript 3.9+ and "strictNullChecks": true โš 

Usage

import { toZod } from 'tozod';

type Player = {
  name: string;
  age?: number | undefined;
  active: boolean | null;
};

export const Player: toZod<Player> = z.object({
  name: z.string(),
  age: z.number().optional(),
  active: z.boolean().nullable(),
});

Getting rid of any of these method calls will throw a TypeError.

tozod type error screenshot

This gets extremely exciting when you start using it on recursive or mutually recursive types.

type User = {
  id: string;
  name: string;
  age?: number | undefined;
  active: boolean | null;
  posts: Post[];
};

type Post = {
  content: string;
  author: User;
};

export const User: toZod<User> = z.late.object(() => ({
  id: z.string().uuid(), // refinements are fine
  name: z.string(),
  age: z.number().optional(),
  active: z.boolean().nullable(),
  posts: z.array(Post),
}));

export const Post: toZod<Post> = z.late.object(() => ({
  content: z.string(),
  author: User,
}));

The above uses a z.late.object method that is currently implemented but undocumented.

You've just implemented two mutually recursive validatators with accurate static and runtime type information. So you can use Zod's built-in object methods to derive variants of these schemas:

const CreateUserInput = User.omit({ id: true, posts: true });
const PostIdOnly = Post.pick({ id: true });
const UpdateUserInput = User.omit({ id: true }).partial().extend({ id: z.string()u });

And because the TypeScript engine knows the exact shape of the Zod schema internally, you can access its internals like so:

User.shape.posts.element.shape.author;