├── .eslintignore ├── .gitignore ├── .prettierrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── ERROR_HANDLING.md ├── FUNDING.yml ├── LICENSE ├── README.md ├── coverage.svg ├── jestconfig.json ├── logo.svg ├── package.json ├── src ├── index.ts └── playground.ts ├── tsconfig.json ├── tsconfig.package.json ├── tslint.json ├── yarn-error.log └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/tozod/b3bb0cb750cb72e78f238432b9ae944d0cbf3283/.eslintignore -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | **/.DS_Store 3 | node_modules 4 | /lib 5 | /coverage 6 | .vscode 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "trailingComma": "all", 4 | "singleQuote": true 5 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | | zod version | release notes | 4 | | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 5 | | zod@1.9 | Added z.instanceof() and z.custom(). Implemented ZodSchema.array() method. | 6 | | zod@1.8 | Introduced z.void(). Major overhaul to error handling system, including the introduction of custom error maps. Wrote new [error handling guide](https://github.com/vriad/zod/blob/master/ERROR_HANDLING.md). | 7 | | zod@1.7 | Added several built-in validators to string, number, and array schemas. Calls to `.refine` now return new instance. | 8 | | zod@1.5 | Any and unknown types | 9 | | zod@1.4 | Refinement types (`.refine`), `.parse` no longer returns deep clone | 10 | | zod@1.3 | Promise schemas | 11 | | zod@1.2.6 | `.parse` accepts `unknown`, `bigint` schemas | 12 | | zod@1.2.5 | `.partial` and `.deepPartial` on object schemas | 13 | | zod@1.2.3 | Date schemas | 14 | | zod@1.2.0 | `.pick`, `.omit`, and `.extend` on object schemas | 15 | | zod@1.1.0 | Records | 16 | | zod@1.0.11 | `.nonstrict` | 17 | | zod@1.0.10 | Type assertions with `.check` | 18 | | zod@1.0.4 | Empty tuples | 19 | | zod@1.0.0 | Type assertions, literals, enums, detailed error reporting | 20 | | zod@1.0.0 | Initial release | 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | When it comes to open source, there are different ways you can contribute, all 2 | of which are valuable. Here's few guidelines that should help you as you prepare 3 | your contribution. 4 | 5 | ## Initial steps 6 | 7 | Before you start working on a contribution, create an issue describing what you want to build. It's possible someone else is already working on something similar, or perhaps there is a reason that feature isn't implemented. The maintainers will point you in the right direction. 8 | 9 | 19 | 20 | ## Development 21 | 22 | The following steps will get you setup to contribute changes to this repo: 23 | 24 | 1. Fork this repo. 25 | 26 | 2. Clone your forked repo: `git clone git@github.com:{your_username}/zod.git` 27 | 28 | 3. Run `yarn` to install dependencies. 29 | 30 | 4. Start playing with the code! You can do some simple experimentation in `src/playground.ts` (see `yarn play` below) or start implementing a feature right away. 31 | 32 | ### Commands 33 | 34 | **`yarn build`** 35 | 36 | - deletes `lib` and re-compiles `src` to `lib` 37 | 38 | **`yarn test`** 39 | 40 | - runs all Jest tests and generates coverage badge 41 | 42 | **`yarn testone`** 43 | 44 | - runs a single test file 45 | - example: `yarn testone src/__tests__/testFileNameHere.ts` 46 | 47 | **`yarn play`** 48 | 49 | - executes `src/playground.ts`, watches for changes. useful for experimentation 50 | 51 | ### Tests 52 | 53 | Zod uses Jest for testing. After implementing your contribution, write tests for it. Just create a new file under `src/__tests__` or add additional tests to the appropriate existing file. 54 | 55 | Before submitting your PR, run `yarn test` to make sure there are no (unintended) breaking changes. 56 | 57 | ### Documentation 58 | 59 | The Zod documentation lives in the README.md. Be sure to document any API changes you implement. 60 | 61 | ## License 62 | 63 | By contributing your code to the zod GitHub repository, you agree to 64 | license your contribution under the MIT license. 65 | -------------------------------------------------------------------------------- /ERROR_HANDLING.md: -------------------------------------------------------------------------------- 1 | # Error Handling in Zod 2 | 3 | This guide explains Zod's internal error handling system, and the various ways you can customize it for your purposes. 4 | 5 | ## ZodError 6 | 7 | All validation errors thrown by Zod are instances of `ZodError`. 8 | 9 | ```ts 10 | class ZodError extends Error { 11 | errors: ZodSuberror[]; 12 | } 13 | ``` 14 | 15 | ZodError is a subclass of `Error`; if you want to place around with this class, you can create an instance like so: 16 | 17 | ```ts 18 | import * as z from 'zod'; 19 | 20 | const myError = new z.ZodError([]); 21 | ``` 22 | 23 | ZodErrors are just a wrapper class that contain information about the problems that occurred during validation. The list of issues are contained in the `errors` property. 24 | 25 | ## ZodSuberror 26 | 27 | `ZodSuberror` is _not_ a class. It is a [discriminated union](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions). 28 | 29 | The link above the the best way to learn about the concept. Discriminated unions are an ideal way to represent a data structures that may be one of many possible variants. 30 | 31 | Every ZodSuberror has these fields: 32 | 33 | | field | type | details | 34 | | --------- | ---------------------- | ------------------------------------------------------------------------------------------------- | 35 | | `code` | `z.ZodErrorCode` | You can access this enum with `z.ZodErrorCode`. A full breakdown of the possible values is below. | 36 | | `path` | `(string \| number)[]` | e.g, `['addresses', 0, 'line1']` | 37 | | `message` | `string` | e.g. `Invalid type. Expected string, received number.` | 38 | 39 | **However** depending on the error code, there may be additional properties as well. Here is a full breakdown of the additional fields by error code: 40 | 41 | ## ZodErrorCode 42 | 43 | | code | additional fields | 44 | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 45 | | ZodErrorCode.invalid_type | `expected: ZodParsedType`
`received: ZodParsedType`

Jump to [this section](#parsedtype) for a breakdownthe possible values of ZodParsedType. | 46 | | ZodErrorCode.nonempty_array_is_empty | _no additional properties_ | 47 | | ZodErrorCode.unrecognized_keys | `keys: string[]`
The list of unrecognized keys
| 48 | | ZodErrorCode.invalid_union | `unionErrors: ZodError[]`
The errors thrown by each element of the union. | 49 | | ZodErrorCode.invalid_literal_value | `expected: string \| number \| boolean`
The literal value. | 50 | | ZodErrorCode.invalid_enum_value | `options: string[]`
The set of acceptable string values for this enum. | 51 | | ZodErrorCode.invalid_arguments | `argumentsError: ZodError`
This is a special error code only thrown by a wrapped function returned by `ZodFunction.implement()`. The `argumentsError` property is another ZodError containing the validation error details. | 52 | | ZodErrorCode.invalid_return_type | `returnTypeError: ZodError`
This is a special error code only thrown by a wrapped function returned by `ZodFunction.implement()`. The `returnTypeError` property is another ZodError containing the validation error details. | 53 | | ZodErrorCode.invalid_date | _no additional properties_ | 54 | | ZodErrorCode.invalid_string | `validation: "url" \| "email" \| "uuid"`
Which built-in string validator failed | 55 | | ZodErrorCode.too_small | `type: "string" \| "number" \| "array"`
The type of the data failing validation

`minimum: number`
The expected length/value.

`inclusive: boolean`
Whether the minimum is included in the range of acceptable values.
| 56 | | ZodErrorCode.too_big | `type: "string" \| "number" \| "array"`
The type of the data failing validation

`maximum: number`
The expected length/value.

`inclusive: boolean`
Whether the minimum is included in the range of acceptable values.
| 57 | | ZodErrorCode.custom_error | `params: { [k: string]: any }`
This is the error code throw by **all custom refinements**. You are able to pass in a `params` object here that is available in your custom error maps (see [ZodErrorMap](#Customizing-errors-with-ZodErrorMap) below for details on error maps) | 58 | 59 | ## ZodParsedType 60 | 61 | This is an enum used byn Zod internally to represent the type of a parsed value. The possible values are: 62 | 63 | - `string` 64 | - `nan` 65 | - `number` 66 | - `integer` 67 | - `boolean` 68 | - `date` 69 | - `bigint` 70 | - `symbol` 71 | - `function` 72 | - `undefined` 73 | - `null` 74 | - `array` 75 | - `object` 76 | - `unknown` 77 | - `promise` 78 | - `void` 79 | 80 | ## A demonstrative example 81 | 82 | Here's a sample Person schema. 83 | 84 | ```ts 85 | const person = z.object({ 86 | names: z.array(z.string()).nonempty(), // at least 1 name 87 | address: z.object({ 88 | line1: z.string(), 89 | zipCode: z.number().min(10000), // American 5-digit code 90 | }), 91 | }); 92 | ``` 93 | 94 | Let's pass in some improperly formatted data. 95 | 96 | ```ts 97 | try { 98 | person.parse({ 99 | names: ['Dave', 12], // 12 is not a string 100 | address: { 101 | line1: '123 Maple Ave', 102 | zipCode: 123, // zip code isnt 5 digits 103 | extra: 'other stuff', // unrecognized key 104 | }, 105 | }); 106 | } catch (err) { 107 | if (err instanceof z.ZodError) { 108 | console.log(err.errors); 109 | } 110 | } 111 | ``` 112 | 113 | Here are the errors that will be printed: 114 | 115 | ```ts 116 | [ 117 | { 118 | code: 'invalid_type', 119 | expected: 'string', 120 | received: 'number', 121 | path: ['names', 1], 122 | message: 'Invalid input: expected string, received number', 123 | }, 124 | { 125 | code: 'unrecognized_keys', 126 | keys: ['extra'], 127 | path: ['address'], 128 | message: "Unrecognized key(s) in object: 'extra'", 129 | }, 130 | { 131 | code: 'too_small', 132 | minimum: 10000, 133 | type: 'number', 134 | inclusive: true, 135 | path: ['address', 'zipCode'], 136 | message: 'Value should be greater than or equal to 10000', 137 | }, 138 | ]; 139 | ``` 140 | 141 | As you can see three different issues were identified. Every ZodSuberror has a `code` property and additional metadata about the validation failure. For instance the `unrecognized_keys` error provides a list of the unrecognized keys detected in the input. 142 | 143 | ## Customizing errors with ZodErrorMap 144 | 145 | You can customize **all** error messages produced by Zod by providing a custom instance of ZodErrorMap to `.parse()`. Internally, Zod uses a [default error map](https://github.com/vriad/zod/blob/master/defaultErrorMap.ts) to produce all error messages. 146 | 147 | `ZodErrorMap` is a special function. It accepts two arguments: `error` and `ctx`. The return type is `{ message: string }`. Essentially the error map accepts some information about the validation that is failing and returns an appropriate error message. 148 | 149 | - `error: Omit` 150 | 151 | As mentioned above, ZodSuberror is a discriminated union. 152 | 153 | - `ctx: { defaultError: string; data: any }` 154 | 155 | - `ctx.default` is the error message generated by the default error map. If you only want to override the message for a single type of error, you can do that. Just return `defaultError` for everything 156 | 157 | - `ctx.data` contains the data that was passed into `.parse`. You can use this to customize the error message. 158 | 159 | ### A working example 160 | 161 | Let's look at a practical example of of customized error map: 162 | 163 | ```ts 164 | import * as z from 'zod'; 165 | 166 | const errorMap: z.ZodErrorMap = (error, ctx) => { 167 | /* 168 | 169 | If error.message is set, that means the user is trying to 170 | override the error message. This is how method-specific 171 | error overrides work, like this: 172 | 173 | z.string().min(5, { message: "TOO SMALL 🤬" }) 174 | 175 | It is a best practice to return `error.message` if it is set. 176 | 177 | */ 178 | if (error.message) return { message: error.message }; 179 | 180 | /* 181 | This is where you override the various error codes 182 | */ 183 | switch (error.code) { 184 | case z.ZodErrorCode.invalid_type: 185 | if (error.expected === 'string') { 186 | return { message: `This ain't a string!` }; 187 | } 188 | break; 189 | case z.ZodErrorCode.custom_error: 190 | // produce a custom message using error.params 191 | // error.params won't be set unless you passed 192 | // a `params` arguments into a custom validator 193 | const params = error.params || {}; 194 | if (params.myField) { 195 | return { message: `Bad input: ${params.myField}` }; 196 | } 197 | break; 198 | } 199 | 200 | // fall back to default message! 201 | return { message: ctx.defaultError }; 202 | }; 203 | 204 | z.string().parse(12, { errorMap }); 205 | 206 | /* throws: 207 | ZodError { 208 | errors: [{ 209 | code: "invalid_type", 210 | path: [], 211 | message: "This ain't a string!", 212 | expected: "string", 213 | received: "number", 214 | }] 215 | } 216 | */ 217 | ``` 218 | 219 | ## Error handling for forms 220 | 221 | If you're using Zod to validate the inputs from a web form, there is a convenient way to "flatten" a ZodError to a format that can be easily displayed to the end user. 222 | 223 | Consider this example of a simple signup form: 224 | 225 | ```ts 226 | const FormData = z 227 | .object({ 228 | email: z.string().email(), 229 | password: z.string().min(10), 230 | confirm: z.string().min(10), 231 | }) 232 | .refine(obj => obj.password === obj.confirm, { 233 | message: 'Passwords do not match', 234 | path: ['confirm'], // sets the path of the error thrown by this refinement 235 | }); 236 | } 237 | ``` 238 | 239 | Now lets pass in some invalid data: 240 | 241 | ```ts 242 | FormData.parse({ 243 | email: 'not an email', 244 | password: 'tooshort', 245 | confirm: 'nomatch', 246 | }); 247 | ``` 248 | 249 | This will throw a ZodError with four suberrors: 250 | 251 | ```ts 252 | console.log(err.errors); 253 | /* 254 | [ 255 | { code: 'invalid_string', validation: 'email', path: ['email'], message: 'Invalid email' }, 256 | { 257 | code: 'too_small', 258 | minimum: 10, 259 | type: 'string', 260 | inclusive: true, 261 | path: ['password'], 262 | message: 'Should be at least 10 characters', 263 | }, 264 | { 265 | code: 'too_small', 266 | minimum: 10, 267 | type: 'string', 268 | inclusive: true, 269 | path: ['confirm'], 270 | message: 'Should be at least 10 characters', 271 | }, 272 | { code: 'custom_error', message: 'Passwords do not match', path: ['confirm'] }, 273 | ]; 274 | */ 275 | ``` 276 | 277 | But using the `formErrors` property, we can "flatten" all those errors down to a form that's much easier to work with: 278 | 279 | ```ts 280 | console.log(err.formErrors); 281 | /* 282 | { 283 | formErrors: [], 284 | fieldErrors: { 285 | email: ['Invalid email'], 286 | password: ['Should be at least 10 characters'], 287 | confirm: ['Should be at least 10 characters', 'Passwords do not match'], 288 | }, 289 | } 290 | ``` 291 | 292 | - `fieldErrors` is an object. The keys are the field(s) that threw the error. The values are an array of error strings that can be easily presented in the interface. 293 | - `formErrors: string[]` is an array of errors that occured on the root of the form schema. For instance if you called `FormData.parse(null)`, `formErrors` would be: 294 | ```ts 295 | ['Invalid input: expected object, received null']; 296 | ``` 297 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: vriad -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Colin McDonnell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

toZod

2 | 3 | `toZod` is a utility for defining Zod schemas that agree with a TypeScript type. 4 | 5 | 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. 6 | 7 | ## Installation 8 | 9 | ```ts 10 | yarn add tozod 11 | ``` 12 | 13 | ⚠ Requires TypeScript 3.9+ and `"strictNullChecks": true` ⚠ 14 | 15 | ## Usage 16 | 17 | ```ts 18 | import { toZod } from 'tozod'; 19 | 20 | type Player = { 21 | name: string; 22 | age?: number | undefined; 23 | active: boolean | null; 24 | }; 25 | 26 | export const Player: toZod = z.object({ 27 | name: z.string(), 28 | age: z.number().optional(), 29 | active: z.boolean().nullable(), 30 | }); 31 | ``` 32 | 33 | Getting rid of any of these method calls will throw a TypeError. 34 | 35 | ![tozod type error screenshot](https://i.imgur.com/XWdXWRw.png) 36 | 37 | This gets extremely exciting when you start using it on recursive or mutually recursive types. 38 | 39 | ```ts 40 | type User = { 41 | id: string; 42 | name: string; 43 | age?: number | undefined; 44 | active: boolean | null; 45 | posts: Post[]; 46 | }; 47 | 48 | type Post = { 49 | content: string; 50 | author: User; 51 | }; 52 | 53 | export const User: toZod = z.late.object(() => ({ 54 | id: z.string().uuid(), // refinements are fine 55 | name: z.string(), 56 | age: z.number().optional(), 57 | active: z.boolean().nullable(), 58 | posts: z.array(Post), 59 | })); 60 | 61 | export const Post: toZod = z.late.object(() => ({ 62 | content: z.string(), 63 | author: User, 64 | })); 65 | ``` 66 | 67 | > The above uses a z.late.object method that is currently implemented but undocumented. 68 | 69 | 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: 70 | 71 | ```ts 72 | const CreateUserInput = User.omit({ id: true, posts: true }); 73 | const PostIdOnly = Post.pick({ id: true }); 74 | const UpdateUserInput = User.omit({ id: true }).partial().extend({ id: z.string()u }); 75 | ``` 76 | 77 | And because the TypeScript engine knows the exact shape of the Zod schema internally, you can access its internals like so: 78 | 79 | ```ts 80 | User.shape.posts.element.shape.author; 81 | ``` 82 | 83 | 84 | -------------------------------------------------------------------------------- /coverage.svg: -------------------------------------------------------------------------------- 1 | Coverage: 93.88%Coverage93.88% -------------------------------------------------------------------------------- /jestconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "transform": { 3 | "^.+\\.(t|j)sx?$": "ts-jest" 4 | }, 5 | "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", 6 | "moduleFileExtensions": [ 7 | "ts", 8 | "tsx", 9 | "js", 10 | "jsx", 11 | "json", 12 | "node" 13 | ], 14 | "coverageReporters": [ 15 | "json-summary", 16 | "text", 17 | "lcov" 18 | ] 19 | } -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tozod", 3 | "version": "0.0.1", 4 | "description": "Inferring Zod schema from TypeScript types", 5 | "main": "./lib/src/index.js", 6 | "types": "./lib/src/index.d.ts", 7 | "files": [ 8 | "lib" 9 | ], 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/vriad/tozod" 13 | }, 14 | "author": "Colin McDonnell ", 15 | "license": "MIT", 16 | "sideEffects": false, 17 | "bugs": { 18 | "url": "https://github.com/vriad/tozod/issues" 19 | }, 20 | "homepage": "https://github.com/vriad/tozod", 21 | "dependencies": {}, 22 | "scripts": { 23 | "clean": "rm -rf lib/*", 24 | "build": "yarn run clean && tsc --p tsconfig.package.json", 25 | "format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"", 26 | "lint": "tslint -p tsconfig.json", 27 | "test": "jest --config jestconfig.json --coverage && yarn run badge", 28 | "testone": "jest --config jestconfig.json ", 29 | "badge": "make-coverage-badge --output-path ./coverage.svg", 30 | "prepublishOnly": "npm run build", 31 | "play": "nodemon -e ts -w . -x ts-node src/playground.ts" 32 | }, 33 | "devDependencies": { 34 | "@types/jest": "^25.1.4", 35 | "jest": "^25.1.0", 36 | "make-coverage-badge": "^1.2.0", 37 | "nodemon": "^2.0.2", 38 | "prettier": "^1.19.1", 39 | "ts-jest": "^25.2.1", 40 | "tslint": "^6.1.0", 41 | "tslint-config-prettier": "^1.18.0", 42 | "typescript": "^4.0.0-dev.20200717", 43 | "zod": "^1.11.8" 44 | }, 45 | "peerDependencies": { 46 | "zod": "^1.9.1" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as z from 'zod'; 2 | 3 | type isAny = [any extends T ? 'true' : 'false'] extends ['true'] ? true : false; 4 | type nonoptional = T extends undefined ? never : T; 5 | type nonnullable = T extends null ? never : T; 6 | type equals = [X] extends [Y] ? ([Y] extends [X] ? true : false) : false; 7 | 8 | export type toZod = { 9 | any: never; 10 | optional: z.ZodUnion<[toZod>, z.ZodUndefined]>; 11 | nullable: z.ZodUnion<[toZod>, z.ZodNull]>; 12 | array: T extends Array ? z.ZodArray> : never; 13 | string: z.ZodString; 14 | bigint: z.ZodBigInt; 15 | number: z.ZodNumber; 16 | boolean: z.ZodBoolean; 17 | date: z.ZodDate; 18 | object: z.ZodObject<{ [k in keyof T]: toZod }, { strict: true }, T>; 19 | rest: never; 20 | }[zodKey]; 21 | 22 | type zodKey = isAny extends true 23 | ? 'any' 24 | : equals extends true //[T] extends [booleanUtil.Type] 25 | ? 'boolean' 26 | : [undefined] extends [T] 27 | ? 'optional' 28 | : [null] extends [T] 29 | ? 'nullable' 30 | : T extends any[] 31 | ? 'array' 32 | : equals extends true 33 | ? 'string' 34 | : equals extends true //[T] extends [bigintUtil.Type] 35 | ? 'bigint' 36 | : equals extends true //[T] extends [numberUtil.Type] 37 | ? 'number' 38 | : equals extends true //[T] extends [dateUtil.Type] 39 | ? 'date' 40 | : T extends { [k: string]: any } //[T] extends [structUtil.Type] 41 | ? 'object' 42 | : 'rest'; 43 | -------------------------------------------------------------------------------- /src/playground.ts: -------------------------------------------------------------------------------- 1 | import * as z from 'zod'; 2 | import { toZod } from '.'; 3 | 4 | type Player = { 5 | name: string; 6 | age?: number | undefined; 7 | active: boolean | null; 8 | }; 9 | 10 | export const Player: toZod = z.object({ 11 | name: z.string(), 12 | age: z.number().optional(), 13 | active: z.boolean().nullable(), 14 | }); 15 | 16 | type User = { 17 | name: string; 18 | age?: number | undefined; 19 | active: boolean | null; 20 | posts: Post[]; 21 | }; 22 | 23 | type Post = { 24 | content: string; 25 | author: User; 26 | }; 27 | 28 | const User: toZod = z.late.object(() => ({ 29 | name: z 30 | .string() 31 | .min(5) 32 | .max(2314) 33 | .refine(() => false, 'asdf'), 34 | age: z.number().optional(), 35 | active: z.boolean().nullable(), 36 | 37 | posts: z.array(Post), 38 | })); 39 | 40 | const Post: toZod = z.late.object(() => ({ 41 | content: z.string(), 42 | author: User, 43 | })); 44 | 45 | console.log(User.shape.posts.element.shape.author); 46 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "es5", 5 | "es6", 6 | "es7", 7 | "esnext", 8 | "dom" 9 | ], 10 | "target": "es5", 11 | "module": "commonjs", 12 | "declaration": true, 13 | "outDir": "./lib", 14 | "allowUnreachableCode": false, 15 | "allowUnusedLabels": false, 16 | "esModuleInterop": true, 17 | "emitDecoratorMetadata": true, 18 | "experimentalDecorators": true, 19 | "sourceMap": true, 20 | "resolveJsonModule": true, 21 | "strict": true, 22 | "noImplicitAny": true, 23 | "strictNullChecks": true, 24 | "noImplicitThis": true, 25 | "alwaysStrict": true, 26 | "strictPropertyInitialization": false, 27 | "noUnusedLocals": true, 28 | "noUnusedParameters": true, 29 | "noImplicitReturns": true, 30 | "noFallthroughCasesInSwitch": true, 31 | }, 32 | "include": [ 33 | "src", 34 | "tsconfig.package.json" 35 | ], 36 | "exclude": [ 37 | "node_modules", 38 | ] 39 | } -------------------------------------------------------------------------------- /tsconfig.package.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "node_modules", 5 | "**/__tests__/" 6 | ] 7 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-config-prettier" 5 | ] 6 | } --------------------------------------------------------------------------------