├── .eslintignore
├── .nojekyll
├── .husky
├── .gitignore
├── pre-push
└── pre-commit
├── FUNDING.yml
├── robots.txt
├── playground.ts
├── index.d.ts
├── _redirects
├── static
├── favicon.ico
├── favicon-16x16.png
├── favicon-32x32.png
├── mstile-70x70.png
├── mstile-144x144.png
├── mstile-150x150.png
├── mstile-310x150.png
├── mstile-310x310.png
├── apple-touch-icon.png
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── moon.svg
├── browserconfig.xml
├── site.webmanifest
└── sun.svg
├── deno
└── lib
│ ├── mod.ts
│ ├── index.ts
│ ├── helpers
│ ├── typeAliases.ts
│ ├── errorUtil.ts
│ ├── enumUtil.ts
│ └── partialUtil.ts
│ ├── external.ts
│ ├── errors.ts
│ ├── __tests__
│ ├── void.test.ts
│ ├── mocker.test.ts
│ ├── custom.test.ts
│ ├── nan.test.ts
│ ├── complex.test.ts
│ ├── object-augmentation.test.ts
│ ├── safeparse.test.ts
│ ├── masking.test.ts
│ ├── anyunknown.test.ts
│ ├── base.test.ts
│ ├── firstpartyschematypes.test.ts
│ ├── pipeline.test.ts
│ ├── parseUtil.test.ts
│ ├── date.test.ts
│ ├── literal.test.ts
│ ├── instanceof.test.ts
│ ├── nullable.test.ts
│ ├── Mocker.ts
│ ├── parser.test.ts
│ ├── optional.test.ts
│ ├── description.test.ts
│ ├── crazySchema.ts
│ ├── generics.test.ts
│ ├── async-refinements.test.ts
│ ├── unions.test.ts
│ ├── branded.test.ts
│ ├── bigint.test.ts
│ ├── array.test.ts
│ ├── nativeEnum.test.ts
│ ├── firstparty.test.ts
│ ├── standard-schema.test.ts
│ ├── promise.test.ts
│ ├── enum.test.ts
│ ├── tuple.test.ts
│ ├── intersection.test.ts
│ ├── map.test.ts
│ ├── default.test.ts
│ └── pickomit.test.ts
│ ├── benchmarks
│ ├── realworld.ts
│ ├── string.ts
│ ├── index.ts
│ ├── union.ts
│ ├── object.ts
│ ├── discriminatedUnion.ts
│ ├── ipv4.ts
│ └── datetime.ts
│ └── standard-schema.ts
├── src
├── index.ts
├── helpers
│ ├── typeAliases.ts
│ ├── errorUtil.ts
│ ├── enumUtil.ts
│ └── partialUtil.ts
├── external.ts
├── errors.ts
├── __tests__
│ ├── void.test.ts
│ ├── mocker.test.ts
│ ├── custom.test.ts
│ ├── nan.test.ts
│ ├── complex.test.ts
│ ├── object-augmentation.test.ts
│ ├── safeparse.test.ts
│ ├── masking.test.ts
│ ├── anyunknown.test.ts
│ ├── base.test.ts
│ ├── object-in-es5-env.test.ts
│ ├── firstpartyschematypes.test.ts
│ ├── pipeline.test.ts
│ ├── parseUtil.test.ts
│ ├── date.test.ts
│ ├── literal.test.ts
│ ├── instanceof.test.ts
│ ├── nullable.test.ts
│ ├── parser.test.ts
│ ├── optional.test.ts
│ ├── Mocker.ts
│ ├── description.test.ts
│ ├── crazySchema.ts
│ ├── language-server.source.ts
│ ├── generics.test.ts
│ ├── async-refinements.test.ts
│ ├── unions.test.ts
│ ├── branded.test.ts
│ ├── bigint.test.ts
│ ├── array.test.ts
│ ├── nativeEnum.test.ts
│ ├── firstparty.test.ts
│ ├── standard-schema.test.ts
│ ├── promise.test.ts
│ ├── enum.test.ts
│ ├── tuple.test.ts
│ ├── intersection.test.ts
│ ├── map.test.ts
│ ├── default.test.ts
│ └── pickomit.test.ts
├── benchmarks
│ ├── realworld.ts
│ ├── string.ts
│ ├── index.ts
│ ├── union.ts
│ ├── object.ts
│ ├── discriminatedUnion.ts
│ ├── ipv4.ts
│ └── datetime.ts
└── standard-schema.ts
├── tsconfig.json
├── FUNDING.json
├── .prettierrc.yaml
├── tea.yaml
├── configs
├── babel.config.js
├── vitest.config.ts
├── tsconfig.types.json
├── tsconfig.cjs.json
├── tsconfig.esm.json
├── tsconfig.test.json
├── jest.config.json
├── ts-jest.config.json
├── swc-jest.config.json
├── babel-jest.config.json
├── tsconfig.base.json
└── rollup.config.js
├── .gitignore
├── .editorconfig
├── jest.config.json
├── .vscode
├── launch.json
└── settings.json
├── .devcontainer
└── devcontainer.json
├── LICENSE
├── .github
└── workflows
│ ├── release-canary.yml
│ └── test.yml
├── .eslintrc.js
├── deno-build.mjs
└── CONTRIBUTING.md
/.eslintignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.nojekyll:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: colinhacks
2 |
--------------------------------------------------------------------------------
/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/playground.ts:
--------------------------------------------------------------------------------
1 | import { z } from "./src";
2 |
3 | z;
4 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from "./lib";
2 | export as namespace Zod;
3 |
--------------------------------------------------------------------------------
/_redirects:
--------------------------------------------------------------------------------
1 | /blog/* /blog/index.html 200
2 | /* / 200
3 |
--------------------------------------------------------------------------------
/.husky/pre-push:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | yarn test
5 |
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iKowalchuk/zod/HEAD/static/favicon.ico
--------------------------------------------------------------------------------
/static/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iKowalchuk/zod/HEAD/static/favicon-16x16.png
--------------------------------------------------------------------------------
/static/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iKowalchuk/zod/HEAD/static/favicon-32x32.png
--------------------------------------------------------------------------------
/static/mstile-70x70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iKowalchuk/zod/HEAD/static/mstile-70x70.png
--------------------------------------------------------------------------------
/deno/lib/mod.ts:
--------------------------------------------------------------------------------
1 | export * from "./index.ts";
2 | export { default as default } from "./index.ts";
3 |
--------------------------------------------------------------------------------
/static/mstile-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iKowalchuk/zod/HEAD/static/mstile-144x144.png
--------------------------------------------------------------------------------
/static/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iKowalchuk/zod/HEAD/static/mstile-150x150.png
--------------------------------------------------------------------------------
/static/mstile-310x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iKowalchuk/zod/HEAD/static/mstile-310x150.png
--------------------------------------------------------------------------------
/static/mstile-310x310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iKowalchuk/zod/HEAD/static/mstile-310x310.png
--------------------------------------------------------------------------------
/static/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iKowalchuk/zod/HEAD/static/apple-touch-icon.png
--------------------------------------------------------------------------------
/static/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iKowalchuk/zod/HEAD/static/android-chrome-192x192.png
--------------------------------------------------------------------------------
/static/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iKowalchuk/zod/HEAD/static/android-chrome-512x512.png
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as z from "./external";
2 | export * from "./external";
3 | export { z };
4 | export default z;
5 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 | yarn build:deno
6 | git add deno
7 |
--------------------------------------------------------------------------------
/deno/lib/index.ts:
--------------------------------------------------------------------------------
1 | import * as z from "./external.ts";
2 | export * from "./external.ts";
3 | export { z };
4 | export default z;
5 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./configs/tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../lib"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/FUNDING.json:
--------------------------------------------------------------------------------
1 | {
2 | "drips": {
3 | "ethereum": {
4 | "ownedBy": "0xAe9ae688557471b0317734a54bE095b3C675DA2f"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/.prettierrc.yaml:
--------------------------------------------------------------------------------
1 | # .prettierrc or .prettierrc.yaml
2 | trailingComma: "es5"
3 | tabWidth: 2
4 | semi: true
5 | singleQuote: false
6 | printWidth: 80
7 |
--------------------------------------------------------------------------------
/tea.yaml:
--------------------------------------------------------------------------------
1 | # https://tea.xyz/what-is-this-file
2 | ---
3 | version: 1.0.0
4 | codeOwners:
5 | - '0xF233A42130Bcdd8b22FFB5D9593199f31C3Eeb87'
6 | quorum: 1
7 |
--------------------------------------------------------------------------------
/configs/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | ["@babel/preset-env", { targets: { node: "current" } }],
4 | "@babel/preset-typescript",
5 | ],
6 | };
7 |
--------------------------------------------------------------------------------
/src/helpers/typeAliases.ts:
--------------------------------------------------------------------------------
1 | export type Primitive =
2 | | string
3 | | number
4 | | symbol
5 | | bigint
6 | | boolean
7 | | null
8 | | undefined;
9 | export type Scalars = Primitive | Primitive[];
10 |
--------------------------------------------------------------------------------
/deno/lib/helpers/typeAliases.ts:
--------------------------------------------------------------------------------
1 | export type Primitive =
2 | | string
3 | | number
4 | | symbol
5 | | bigint
6 | | boolean
7 | | null
8 | | undefined;
9 | export type Scalars = Primitive | Primitive[];
10 |
--------------------------------------------------------------------------------
/src/external.ts:
--------------------------------------------------------------------------------
1 | export * from "./errors";
2 | export * from "./helpers/parseUtil";
3 | export * from "./helpers/typeAliases";
4 | export * from "./helpers/util";
5 | export * from "./types";
6 | export * from "./ZodError";
7 |
--------------------------------------------------------------------------------
/deno/lib/external.ts:
--------------------------------------------------------------------------------
1 | export * from "./errors.ts";
2 | export * from "./helpers/parseUtil.ts";
3 | export * from "./helpers/typeAliases.ts";
4 | export * from "./helpers/util.ts";
5 | export * from "./types.ts";
6 | export * from "./ZodError.ts";
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/.DS_Store
2 | node_modules
3 | /lib
4 | /__buildtest__
5 | coverage
6 | .idea
7 | *.log
8 | playground.ts
9 | playground.mts
10 | deno/lib/playground.ts
11 | .eslintcache
12 | workspace.code-workspace
13 | .netlify
14 | bun.lockb
15 |
--------------------------------------------------------------------------------
/static/moon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/configs/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vitest/config";
2 |
3 | export default defineConfig({
4 | test: {
5 | alias: {
6 | "@jest/globals": "vitest",
7 | },
8 | include: ["src/**/*.test.ts"],
9 | isolate: false,
10 | watch: false,
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/static/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #da532c
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/configs/tsconfig.types.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../lib/types",
5 | "declaration": true,
6 | "declarationMap": true,
7 | "emitDeclarationOnly": true
8 | },
9 | "exclude": ["../src/**/__tests__", "../src/playground.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | end_of_line = lf
9 | insert_final_newline = true
10 | indent_style = space
11 | indent_size = 2
12 | charset = utf-8
13 |
--------------------------------------------------------------------------------
/configs/tsconfig.cjs.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "outDir": "../lib",
6 | "declaration": true,
7 | "declarationMap": false,
8 | "sourceMap": false
9 | },
10 | "exclude": ["../src/**/__tests__", "../playground.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/configs/tsconfig.esm.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "es2015",
5 | // "outDir": "./lib/esm",
6 | "declaration": false,
7 | "declarationMap": false,
8 | "sourceMap": false
9 | },
10 | "exclude": ["../src/**/__tests__", "../src/playground.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/configs/tsconfig.test.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "outDir": "__buildtest__",
6 | "target": "es5",
7 | "declaration": true,
8 | "declarationMap": false,
9 | "sourceMap": false,
10 | "noEmit": true
11 | },
12 | "exclude": []
13 | }
14 |
--------------------------------------------------------------------------------
/jest.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "rootDir": ".",
3 | "transform": {
4 | "^.+\\.tsx?$": "ts-jest"
5 | },
6 | "testRegex": "src/.*\\.test\\.ts$",
7 | "modulePathIgnorePatterns": ["language-server", "__vitest__"],
8 | "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
9 | "coverageReporters": ["json-summary", "text", "lcov"]
10 | }
11 |
--------------------------------------------------------------------------------
/configs/jest.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "rootDir": "..",
3 | "transform": {
4 | "^.+\\.tsx?$": "ts-jest"
5 | },
6 | "testRegex": "src/.*\\.test\\.ts$",
7 | "modulePathIgnorePatterns": ["language-server", "__vitest__"],
8 | "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
9 | "coverageReporters": ["json-summary", "text", "lcov"]
10 | }
11 |
--------------------------------------------------------------------------------
/src/errors.ts:
--------------------------------------------------------------------------------
1 | import defaultErrorMap from "./locales/en";
2 | import type { ZodErrorMap } from "./ZodError";
3 |
4 | let overrideErrorMap = defaultErrorMap;
5 | export { defaultErrorMap };
6 |
7 | export function setErrorMap(map: ZodErrorMap) {
8 | overrideErrorMap = map;
9 | }
10 |
11 | export function getErrorMap() {
12 | return overrideErrorMap;
13 | }
14 |
--------------------------------------------------------------------------------
/configs/ts-jest.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "rootDir": "..",
3 | "transform": {
4 | "^.+\\.tsx?$": "ts-jest"
5 | },
6 | "testRegex": "src/.*\\.test\\.ts$",
7 | "modulePathIgnorePatterns": ["language-server", "__vitest__"],
8 | "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
9 | "coverageReporters": ["json-summary", "text", "lcov"]
10 | }
11 |
--------------------------------------------------------------------------------
/configs/swc-jest.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "rootDir": "..",
3 | "transform": {
4 | "^.+\\.(t|j)sx?$": "@swc/jest"
5 | },
6 | "testRegex": "src/.*\\.test\\.ts$",
7 | "modulePathIgnorePatterns": ["language-server", "__vitest__"],
8 | "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
9 | "coverageReporters": ["json-summary", "text", "lcov"]
10 | }
11 |
--------------------------------------------------------------------------------
/deno/lib/errors.ts:
--------------------------------------------------------------------------------
1 | import defaultErrorMap from "./locales/en.ts";
2 | import type { ZodErrorMap } from "./ZodError.ts";
3 |
4 | let overrideErrorMap = defaultErrorMap;
5 | export { defaultErrorMap };
6 |
7 | export function setErrorMap(map: ZodErrorMap) {
8 | overrideErrorMap = map;
9 | }
10 |
11 | export function getErrorMap() {
12 | return overrideErrorMap;
13 | }
14 |
--------------------------------------------------------------------------------
/src/helpers/errorUtil.ts:
--------------------------------------------------------------------------------
1 | export namespace errorUtil {
2 | export type ErrMessage = string | { message?: string };
3 | export const errToObj = (message?: ErrMessage) =>
4 | typeof message === "string" ? { message } : message || {};
5 | export const toString = (message?: ErrMessage): string | undefined =>
6 | typeof message === "string" ? message : message?.message;
7 | }
8 |
--------------------------------------------------------------------------------
/deno/lib/helpers/errorUtil.ts:
--------------------------------------------------------------------------------
1 | export namespace errorUtil {
2 | export type ErrMessage = string | { message?: string };
3 | export const errToObj = (message?: ErrMessage) =>
4 | typeof message === "string" ? { message } : message || {};
5 | export const toString = (message?: ErrMessage): string | undefined =>
6 | typeof message === "string" ? message : message?.message;
7 | }
8 |
--------------------------------------------------------------------------------
/configs/babel-jest.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "rootDir": "..",
3 | "testRegex": "src/.*\\.test\\.ts$",
4 | "transform": {
5 | "^.+\\.tsx?$": ["babel-jest", { "configFile": "./configs/babel.config.js" }]
6 | },
7 | "modulePathIgnorePatterns": ["language-server", "__vitest__"],
8 | "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
9 | "coverageReporters": ["json-summary", "text", "lcov"]
10 | }
11 |
--------------------------------------------------------------------------------
/src/__tests__/void.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import { util } from "../helpers/util";
5 | import * as z from "../index";
6 | test("void", () => {
7 | const v = z.void();
8 | v.parse(undefined);
9 |
10 | expect(() => v.parse(null)).toThrow();
11 | expect(() => v.parse("")).toThrow();
12 |
13 | type v = z.infer;
14 | util.assertEqual(true);
15 | });
16 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/void.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import { util } from "../helpers/util.ts";
6 | import * as z from "../index.ts";
7 | test("void", () => {
8 | const v = z.void();
9 | v.parse(undefined);
10 |
11 | expect(() => v.parse(null)).toThrow();
12 | expect(() => v.parse("")).toThrow();
13 |
14 | type v = z.infer;
15 | util.assertEqual(true);
16 | });
17 |
--------------------------------------------------------------------------------
/src/__tests__/mocker.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import { Mocker } from "./Mocker";
5 |
6 | test("mocker", () => {
7 | const mocker = new Mocker();
8 | mocker.string;
9 | mocker.number;
10 | mocker.boolean;
11 | mocker.null;
12 | mocker.undefined;
13 | mocker.stringOptional;
14 | mocker.stringNullable;
15 | mocker.numberOptional;
16 | mocker.numberNullable;
17 | mocker.booleanOptional;
18 | mocker.booleanNullable;
19 | });
20 |
--------------------------------------------------------------------------------
/static/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Zod",
3 | "short_name": "Zod",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png",
9 | "purpose": "any maskable"
10 | },
11 | {
12 | "src": "/android-chrome-512x512.png",
13 | "sizes": "512x512",
14 | "type": "image/png",
15 | "purpose": "any maskable"
16 | }
17 | ],
18 | "theme_color": "#ffffff",
19 | "background_color": "#ffffff",
20 | "display": "standalone"
21 | }
22 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/mocker.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import { Mocker } from "./Mocker.ts";
6 |
7 | test("mocker", () => {
8 | const mocker = new Mocker();
9 | mocker.string;
10 | mocker.number;
11 | mocker.boolean;
12 | mocker.null;
13 | mocker.undefined;
14 | mocker.stringOptional;
15 | mocker.stringNullable;
16 | mocker.numberOptional;
17 | mocker.numberNullable;
18 | mocker.booleanOptional;
19 | mocker.booleanNullable;
20 | });
21 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "skipFiles": ["/**"],
12 | "preLaunchTask": "tsc: build - tsconfig.json",
13 | "program": "${workspaceFolder}/src/playground.ts",
14 | "outFiles": ["${workspaceFolder}/**/*.js"]
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/src/__tests__/custom.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 |
6 | test("passing validations", () => {
7 | const example1 = z.custom((x) => typeof x === "number");
8 | example1.parse(1234);
9 | expect(() => example1.parse({})).toThrow();
10 | });
11 |
12 | test("string params", () => {
13 | const example1 = z.custom((x) => typeof x !== "number", "customerr");
14 | const result = example1.safeParse(1234);
15 | expect(result.success).toEqual(false);
16 | // @ts-ignore
17 | expect(JSON.stringify(result.error).includes("customerr")).toEqual(true);
18 | });
19 |
--------------------------------------------------------------------------------
/configs/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["es5", "es6", "es7", "esnext", "dom"],
4 | "target": "es2018",
5 | "removeComments": false,
6 | "esModuleInterop": true,
7 | "moduleResolution": "node",
8 | "resolveJsonModule": true,
9 | "strict": true,
10 | "skipLibCheck": true,
11 | "strictPropertyInitialization": false,
12 | "noUnusedLocals": true,
13 | "noUnusedParameters": true,
14 | "noImplicitReturns": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "downlevelIteration": true,
17 | "isolatedModules": true
18 | },
19 | "include": ["../src/**/*", "../playground.ts", "../.eslintrc.js"]
20 | }
21 |
--------------------------------------------------------------------------------
/configs/rollup.config.js:
--------------------------------------------------------------------------------
1 | // rollup.config.js
2 | import typescript from "@rollup/plugin-typescript";
3 |
4 | export default [
5 | {
6 | input: "src/index.ts",
7 | output: [
8 | {
9 | file: "lib/index.mjs",
10 | format: "es",
11 | sourcemap: false,
12 | exports: "named",
13 | },
14 | {
15 | file: "lib/index.umd.js",
16 | name: "Zod",
17 | format: "umd",
18 | sourcemap: false,
19 | exports: "named",
20 | },
21 | ],
22 | plugins: [
23 | typescript({
24 | tsconfig: "./configs/tsconfig.esm.json",
25 | sourceMap: false,
26 | }),
27 | ],
28 | },
29 | ];
30 |
--------------------------------------------------------------------------------
/src/__tests__/nan.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 |
6 | const schema = z.nan();
7 |
8 | test("passing validations", () => {
9 | schema.parse(NaN);
10 | schema.parse(Number("Not a number"));
11 | });
12 |
13 | test("failing validations", () => {
14 | expect(() => schema.parse(5)).toThrow();
15 | expect(() => schema.parse("John")).toThrow();
16 | expect(() => schema.parse(true)).toThrow();
17 | expect(() => schema.parse(null)).toThrow();
18 | expect(() => schema.parse(undefined)).toThrow();
19 | expect(() => schema.parse({})).toThrow();
20 | expect(() => schema.parse([])).toThrow();
21 | });
22 |
--------------------------------------------------------------------------------
/static/sun.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "/Users/colinmcd94/Documents/Projects/zod/node_modules/typescript/lib",
3 | "files.exclude": {
4 | "**/deno": true
5 | },
6 | "files.watcherExclude": {
7 | "**/deno": true
8 | },
9 | "search.exclude": {
10 | "**/deno": true
11 | },
12 | "editor.defaultFormatter": "esbenp.prettier-vscode",
13 | "editor.formatOnSave": true,
14 | "[typescript]": {
15 | "editor.defaultFormatter": "esbenp.prettier-vscode"
16 | },
17 | "[javascript]": {
18 | "editor.defaultFormatter": "esbenp.prettier-vscode"
19 | },
20 | "typescript.validate.enable": true,
21 | "typescript.tsserver.experimental.enableProjectDiagnostics": true
22 | }
23 |
--------------------------------------------------------------------------------
/src/__tests__/complex.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import { crazySchema } from "./crazySchema";
5 | // import * as z from "../index";
6 |
7 | test("parse", () => {
8 | crazySchema.parse({
9 | tuple: ["asdf", 1234, true, null, undefined, "1234"],
10 | merged: { k1: "asdf", k2: 12 },
11 | union: ["asdf", 12, "asdf", 12, "asdf", 12],
12 | array: [12, 15, 16],
13 | // sumTransformer: [12, 15, 16],
14 | sumMinLength: [12, 15, 16, 98, 24, 63],
15 | intersection: {},
16 | enum: "one",
17 | nonstrict: { points: 1234 },
18 | numProm: Promise.resolve(12),
19 | lenfun: (x: string) => x.length,
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/src/helpers/enumUtil.ts:
--------------------------------------------------------------------------------
1 | export namespace enumUtil {
2 | type UnionToIntersectionFn = (
3 | T extends unknown ? (k: () => T) => void : never
4 | ) extends (k: infer Intersection) => void
5 | ? Intersection
6 | : never;
7 |
8 | type GetUnionLast = UnionToIntersectionFn extends () => infer Last
9 | ? Last
10 | : never;
11 |
12 | type UnionToTuple = [T] extends [never]
13 | ? Tuple
14 | : UnionToTuple>, [GetUnionLast, ...Tuple]>;
15 |
16 | type CastToStringTuple = T extends [string, ...string[]] ? T : never;
17 |
18 | export type UnionToTupleString = CastToStringTuple>;
19 | }
20 |
--------------------------------------------------------------------------------
/deno/lib/helpers/enumUtil.ts:
--------------------------------------------------------------------------------
1 | export namespace enumUtil {
2 | type UnionToIntersectionFn = (
3 | T extends unknown ? (k: () => T) => void : never
4 | ) extends (k: infer Intersection) => void
5 | ? Intersection
6 | : never;
7 |
8 | type GetUnionLast = UnionToIntersectionFn extends () => infer Last
9 | ? Last
10 | : never;
11 |
12 | type UnionToTuple = [T] extends [never]
13 | ? Tuple
14 | : UnionToTuple>, [GetUnionLast, ...Tuple]>;
15 |
16 | type CastToStringTuple = T extends [string, ...string[]] ? T : never;
17 |
18 | export type UnionToTupleString = CastToStringTuple>;
19 | }
20 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/custom.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import * as z from "../index.ts";
6 |
7 | test("passing validations", () => {
8 | const example1 = z.custom((x) => typeof x === "number");
9 | example1.parse(1234);
10 | expect(() => example1.parse({})).toThrow();
11 | });
12 |
13 | test("string params", () => {
14 | const example1 = z.custom((x) => typeof x !== "number", "customerr");
15 | const result = example1.safeParse(1234);
16 | expect(result.success).toEqual(false);
17 | // @ts-ignore
18 | expect(JSON.stringify(result.error).includes("customerr")).toEqual(true);
19 | });
20 |
--------------------------------------------------------------------------------
/src/__tests__/object-augmentation.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 |
6 | test("object augmentation", () => {
7 | const Animal = z
8 | .object({
9 | species: z.string(),
10 | })
11 | .augment({
12 | population: z.number(),
13 | });
14 | // overwrites `species`
15 | const ModifiedAnimal = Animal.augment({
16 | species: z.array(z.string()),
17 | });
18 | ModifiedAnimal.parse({
19 | species: ["asd"],
20 | population: 1324,
21 | });
22 |
23 | const bad = () =>
24 | ModifiedAnimal.parse({
25 | species: "asdf",
26 | population: 1324,
27 | } as any);
28 | expect(bad).toThrow();
29 | });
30 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/nan.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import * as z from "../index.ts";
6 |
7 | const schema = z.nan();
8 |
9 | test("passing validations", () => {
10 | schema.parse(NaN);
11 | schema.parse(Number("Not a number"));
12 | });
13 |
14 | test("failing validations", () => {
15 | expect(() => schema.parse(5)).toThrow();
16 | expect(() => schema.parse("John")).toThrow();
17 | expect(() => schema.parse(true)).toThrow();
18 | expect(() => schema.parse(null)).toThrow();
19 | expect(() => schema.parse(undefined)).toThrow();
20 | expect(() => schema.parse({})).toThrow();
21 | expect(() => schema.parse([])).toThrow();
22 | });
23 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/complex.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import { crazySchema } from "./crazySchema.ts";
6 | // import * as z from "../index";
7 |
8 | test("parse", () => {
9 | crazySchema.parse({
10 | tuple: ["asdf", 1234, true, null, undefined, "1234"],
11 | merged: { k1: "asdf", k2: 12 },
12 | union: ["asdf", 12, "asdf", 12, "asdf", 12],
13 | array: [12, 15, 16],
14 | // sumTransformer: [12, 15, 16],
15 | sumMinLength: [12, 15, 16, 98, 24, 63],
16 | intersection: {},
17 | enum: "one",
18 | nonstrict: { points: 1234 },
19 | numProm: Promise.resolve(12),
20 | lenfun: (x: string) => x.length,
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/__tests__/safeparse.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 | const stringSchema = z.string();
6 |
7 | test("safeparse fail", () => {
8 | const safe = stringSchema.safeParse(12);
9 | expect(safe.success).toEqual(false);
10 | expect(safe.error).toBeInstanceOf(z.ZodError);
11 | });
12 |
13 | test("safeparse pass", () => {
14 | const safe = stringSchema.safeParse("12");
15 | expect(safe.success).toEqual(true);
16 | expect(safe.data).toEqual("12");
17 | });
18 |
19 | test("safeparse unexpected error", () => {
20 | expect(() =>
21 | stringSchema
22 | .refine((data) => {
23 | throw new Error(data);
24 | })
25 | .safeParse("12")
26 | ).toThrow();
27 | });
28 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/object-augmentation.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import * as z from "../index.ts";
6 |
7 | test("object augmentation", () => {
8 | const Animal = z
9 | .object({
10 | species: z.string(),
11 | })
12 | .augment({
13 | population: z.number(),
14 | });
15 | // overwrites `species`
16 | const ModifiedAnimal = Animal.augment({
17 | species: z.array(z.string()),
18 | });
19 | ModifiedAnimal.parse({
20 | species: ["asd"],
21 | population: 1324,
22 | });
23 |
24 | const bad = () =>
25 | ModifiedAnimal.parse({
26 | species: "asdf",
27 | population: 1324,
28 | } as any);
29 | expect(bad).toThrow();
30 | });
31 |
--------------------------------------------------------------------------------
/src/__tests__/masking.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 |
6 | test("masking test", () => {});
7 |
8 | test("require", () => {
9 | const baseSchema = z.object({
10 | firstName: z.string(),
11 | middleName: z.string().optional(),
12 | lastName: z.union([z.undefined(), z.string()]),
13 | otherName: z.union([z.string(), z.undefined(), z.string()]),
14 | });
15 | baseSchema;
16 | // const reqBase = baseSchema.require();
17 | // const ewr = reqBase.shape;
18 | // expect(ewr.firstName).toBeInstanceOf(z.ZodString);
19 | // expect(ewr.middleName).toBeInstanceOf(z.ZodString);
20 | // expect(ewr.lastName).toBeInstanceOf(z.ZodString);
21 | // expect(ewr.otherName).toBeInstanceOf(z.ZodUnion);
22 | });
23 |
--------------------------------------------------------------------------------
/src/__tests__/anyunknown.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import { util } from "../helpers/util";
5 | import * as z from "../index";
6 |
7 | test("check any inference", () => {
8 | const t1 = z.any();
9 | t1.optional();
10 | t1.nullable();
11 | type t1 = z.infer;
12 | util.assertEqual(true);
13 | });
14 |
15 | test("check unknown inference", () => {
16 | const t1 = z.unknown();
17 | t1.optional();
18 | t1.nullable();
19 | type t1 = z.infer;
20 | util.assertEqual(true);
21 | });
22 |
23 | test("check never inference", () => {
24 | const t1 = z.never();
25 | expect(() => t1.parse(undefined)).toThrow();
26 | expect(() => t1.parse("asdf")).toThrow();
27 | expect(() => t1.parse(null)).toThrow();
28 | });
29 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/safeparse.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import * as z from "../index.ts";
6 | const stringSchema = z.string();
7 |
8 | test("safeparse fail", () => {
9 | const safe = stringSchema.safeParse(12);
10 | expect(safe.success).toEqual(false);
11 | expect(safe.error).toBeInstanceOf(z.ZodError);
12 | });
13 |
14 | test("safeparse pass", () => {
15 | const safe = stringSchema.safeParse("12");
16 | expect(safe.success).toEqual(true);
17 | expect(safe.data).toEqual("12");
18 | });
19 |
20 | test("safeparse unexpected error", () => {
21 | expect(() =>
22 | stringSchema
23 | .refine((data) => {
24 | throw new Error(data);
25 | })
26 | .safeParse("12")
27 | ).toThrow();
28 | });
29 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/masking.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import * as z from "../index.ts";
6 |
7 | test("masking test", () => {});
8 |
9 | test("require", () => {
10 | const baseSchema = z.object({
11 | firstName: z.string(),
12 | middleName: z.string().optional(),
13 | lastName: z.union([z.undefined(), z.string()]),
14 | otherName: z.union([z.string(), z.undefined(), z.string()]),
15 | });
16 | baseSchema;
17 | // const reqBase = baseSchema.require();
18 | // const ewr = reqBase.shape;
19 | // expect(ewr.firstName).toBeInstanceOf(z.ZodString);
20 | // expect(ewr.middleName).toBeInstanceOf(z.ZodString);
21 | // expect(ewr.lastName).toBeInstanceOf(z.ZodString);
22 | // expect(ewr.otherName).toBeInstanceOf(z.ZodUnion);
23 | });
24 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/anyunknown.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import { util } from "../helpers/util.ts";
6 | import * as z from "../index.ts";
7 |
8 | test("check any inference", () => {
9 | const t1 = z.any();
10 | t1.optional();
11 | t1.nullable();
12 | type t1 = z.infer;
13 | util.assertEqual(true);
14 | });
15 |
16 | test("check unknown inference", () => {
17 | const t1 = z.unknown();
18 | t1.optional();
19 | t1.nullable();
20 | type t1 = z.infer;
21 | util.assertEqual(true);
22 | });
23 |
24 | test("check never inference", () => {
25 | const t1 = z.never();
26 | expect(() => t1.parse(undefined)).toThrow();
27 | expect(() => t1.parse("asdf")).toThrow();
28 | expect(() => t1.parse(null)).toThrow();
29 | });
30 |
--------------------------------------------------------------------------------
/src/__tests__/base.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import { util } from "../helpers/util";
5 | import * as z from "../index";
6 |
7 | test("type guard", () => {
8 | const stringToNumber = z.string().transform((arg) => arg.length);
9 |
10 | const s1 = z.object({
11 | stringToNumber,
12 | });
13 | type t1 = z.input;
14 |
15 | const data = { stringToNumber: "asdf" };
16 | const parsed = s1.safeParse(data);
17 | if (parsed.success) {
18 | util.assertEqual(true);
19 | }
20 | });
21 |
22 | test("test this binding", () => {
23 | const callback = (predicate: (val: string) => boolean) => {
24 | return predicate("hello");
25 | };
26 |
27 | expect(callback((value) => z.string().safeParse(value).success)).toBe(true); // true
28 | expect(callback((value) => z.string().safeParse(value).success)).toBe(true); // true
29 | });
30 |
--------------------------------------------------------------------------------
/src/__tests__/object-in-es5-env.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 |
6 | const RealSet = Set;
7 | const RealMap = Map;
8 | const RealDate = Date;
9 |
10 | test("doesn’t throw when Date is undefined", () => {
11 | delete (globalThis as any).Date;
12 | const result = z.object({}).safeParse({});
13 | expect(result.success).toEqual(true);
14 | globalThis.Date = RealDate;
15 | });
16 |
17 | test("doesn’t throw when Set is undefined", () => {
18 | delete (globalThis as any).Set;
19 | const result = z.object({}).safeParse({});
20 | expect(result.success).toEqual(true);
21 | globalThis.Set = RealSet;
22 | });
23 |
24 | test("doesn’t throw when Map is undefined", () => {
25 | delete (globalThis as any).Map;
26 | const result = z.object({}).safeParse({});
27 | expect(result.success).toEqual(true);
28 | globalThis.Map = RealMap;
29 | });
30 |
--------------------------------------------------------------------------------
/src/__tests__/firstpartyschematypes.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { test } from "@jest/globals";
3 |
4 | import { ZodFirstPartySchemaTypes, ZodFirstPartyTypeKind } from "..";
5 | import { util } from "../helpers/util";
6 |
7 | test("Identify missing [ZodFirstPartySchemaTypes]", () => {
8 | type ZodFirstPartySchemaForType =
9 | ZodFirstPartySchemaTypes extends infer Schema
10 | ? Schema extends { _def: { typeName: T } }
11 | ? Schema
12 | : never
13 | : never;
14 | type ZodMappedTypes = {
15 | [key in ZodFirstPartyTypeKind]: ZodFirstPartySchemaForType;
16 | };
17 | type ZodFirstPartySchemaTypesMissingFromUnion = keyof {
18 | [key in keyof ZodMappedTypes as ZodMappedTypes[key] extends { _def: never }
19 | ? key
20 | : never]: unknown;
21 | };
22 |
23 | util.assertEqual(true);
24 | });
25 |
--------------------------------------------------------------------------------
/src/__tests__/pipeline.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 |
6 | test("string to number pipeline", () => {
7 | const schema = z.string().transform(Number).pipe(z.number());
8 | expect(schema.parse("1234")).toEqual(1234);
9 | });
10 |
11 | test("string to number pipeline async", async () => {
12 | const schema = z
13 | .string()
14 | .transform(async (val) => Number(val))
15 | .pipe(z.number());
16 | expect(await schema.parseAsync("1234")).toEqual(1234);
17 | });
18 |
19 | test("break if dirty", () => {
20 | const schema = z
21 | .string()
22 | .refine((c) => c === "1234")
23 | .transform(async (val) => Number(val))
24 | .pipe(z.number().refine((v) => v < 100));
25 | const r1: any = schema.safeParse("12345");
26 | expect(r1.error.issues.length).toBe(1);
27 | const r2: any = schema.safeParse("3");
28 | expect(r2.error.issues.length).toBe(1);
29 | });
30 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/base.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import { util } from "../helpers/util.ts";
6 | import * as z from "../index.ts";
7 |
8 | test("type guard", () => {
9 | const stringToNumber = z.string().transform((arg) => arg.length);
10 |
11 | const s1 = z.object({
12 | stringToNumber,
13 | });
14 | type t1 = z.input;
15 |
16 | const data = { stringToNumber: "asdf" };
17 | const parsed = s1.safeParse(data);
18 | if (parsed.success) {
19 | util.assertEqual(true);
20 | }
21 | });
22 |
23 | test("test this binding", () => {
24 | const callback = (predicate: (val: string) => boolean) => {
25 | return predicate("hello");
26 | };
27 |
28 | expect(callback((value) => z.string().safeParse(value).success)).toBe(true); // true
29 | expect(callback((value) => z.string().safeParse(value).success)).toBe(true); // true
30 | });
31 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the
2 | // README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
3 | {
4 | "name": "Node.js & TypeScript",
5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6 | "image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bullseye",
7 |
8 | // Features to add to the dev container. More info: https://containers.dev/features.
9 | // "features": {},
10 |
11 | // Use 'forwardPorts' to make a list of ports inside the container available locally.
12 | // "forwardPorts": [],
13 |
14 | // Use 'postCreateCommand' to run commands after the container is created.
15 | "postCreateCommand": "yarn install"
16 |
17 | // Configure tool-specific properties.
18 | // "customizations": {},
19 |
20 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
21 | // "remoteUser": "root"
22 | }
23 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/firstpartyschematypes.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import { ZodFirstPartySchemaTypes, ZodFirstPartyTypeKind } from "../index.ts";
6 | import { util } from "../helpers/util.ts";
7 |
8 | test("Identify missing [ZodFirstPartySchemaTypes]", () => {
9 | type ZodFirstPartySchemaForType =
10 | ZodFirstPartySchemaTypes extends infer Schema
11 | ? Schema extends { _def: { typeName: T } }
12 | ? Schema
13 | : never
14 | : never;
15 | type ZodMappedTypes = {
16 | [key in ZodFirstPartyTypeKind]: ZodFirstPartySchemaForType;
17 | };
18 | type ZodFirstPartySchemaTypesMissingFromUnion = keyof {
19 | [key in keyof ZodMappedTypes as ZodMappedTypes[key] extends { _def: never }
20 | ? key
21 | : never]: unknown;
22 | };
23 |
24 | util.assertEqual(true);
25 | });
26 |
--------------------------------------------------------------------------------
/src/__tests__/parseUtil.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import {
5 | isAborted,
6 | isDirty,
7 | isValid,
8 | SyncParseReturnType,
9 | } from "../helpers/parseUtil";
10 |
11 | test("parseUtil isInvalid should use structural typing", () => {
12 | // Test for issue #556: https://github.com/colinhacks/zod/issues/556
13 | const aborted: SyncParseReturnType = { status: "aborted" };
14 | const dirty: SyncParseReturnType = { status: "dirty", value: "whatever" };
15 | const valid: SyncParseReturnType = { status: "valid", value: "whatever" };
16 |
17 | expect(isAborted(aborted)).toBe(true);
18 | expect(isAborted(dirty)).toBe(false);
19 | expect(isAborted(valid)).toBe(false);
20 |
21 | expect(isDirty(aborted)).toBe(false);
22 | expect(isDirty(dirty)).toBe(true);
23 | expect(isDirty(valid)).toBe(false);
24 |
25 | expect(isValid(aborted)).toBe(false);
26 | expect(isValid(dirty)).toBe(false);
27 | expect(isValid(valid)).toBe(true);
28 | });
29 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/pipeline.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import * as z from "../index.ts";
6 |
7 | test("string to number pipeline", () => {
8 | const schema = z.string().transform(Number).pipe(z.number());
9 | expect(schema.parse("1234")).toEqual(1234);
10 | });
11 |
12 | test("string to number pipeline async", async () => {
13 | const schema = z
14 | .string()
15 | .transform(async (val) => Number(val))
16 | .pipe(z.number());
17 | expect(await schema.parseAsync("1234")).toEqual(1234);
18 | });
19 |
20 | test("break if dirty", () => {
21 | const schema = z
22 | .string()
23 | .refine((c) => c === "1234")
24 | .transform(async (val) => Number(val))
25 | .pipe(z.number().refine((v) => v < 100));
26 | const r1: any = schema.safeParse("12345");
27 | expect(r1.error.issues.length).toBe(1);
28 | const r2: any = schema.safeParse("3");
29 | expect(r2.error.issues.length).toBe(1);
30 | });
31 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/parseUtil.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import {
6 | isAborted,
7 | isDirty,
8 | isValid,
9 | SyncParseReturnType,
10 | } from "../helpers/parseUtil.ts";
11 |
12 | test("parseUtil isInvalid should use structural typing", () => {
13 | // Test for issue #556: https://github.com/colinhacks/zod/issues/556
14 | const aborted: SyncParseReturnType = { status: "aborted" };
15 | const dirty: SyncParseReturnType = { status: "dirty", value: "whatever" };
16 | const valid: SyncParseReturnType = { status: "valid", value: "whatever" };
17 |
18 | expect(isAborted(aborted)).toBe(true);
19 | expect(isAborted(dirty)).toBe(false);
20 | expect(isAborted(valid)).toBe(false);
21 |
22 | expect(isDirty(aborted)).toBe(false);
23 | expect(isDirty(dirty)).toBe(true);
24 | expect(isDirty(valid)).toBe(false);
25 |
26 | expect(isValid(aborted)).toBe(false);
27 | expect(isValid(dirty)).toBe(false);
28 | expect(isValid(valid)).toBe(true);
29 | });
30 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/__tests__/date.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 |
6 | const beforeBenchmarkDate = new Date(2022, 10, 4);
7 | const benchmarkDate = new Date(2022, 10, 5);
8 | const afterBenchmarkDate = new Date(2022, 10, 6);
9 |
10 | const minCheck = z.date().min(benchmarkDate);
11 | const maxCheck = z.date().max(benchmarkDate);
12 |
13 | test("passing validations", () => {
14 | minCheck.parse(benchmarkDate);
15 | minCheck.parse(afterBenchmarkDate);
16 |
17 | maxCheck.parse(benchmarkDate);
18 | maxCheck.parse(beforeBenchmarkDate);
19 | });
20 |
21 | test("failing validations", () => {
22 | expect(() => minCheck.parse(beforeBenchmarkDate)).toThrow();
23 | expect(() => maxCheck.parse(afterBenchmarkDate)).toThrow();
24 | });
25 |
26 | test("min max getters", () => {
27 | expect(minCheck.minDate).toEqual(benchmarkDate);
28 | expect(minCheck.min(afterBenchmarkDate).minDate).toEqual(afterBenchmarkDate);
29 |
30 | expect(maxCheck.maxDate).toEqual(benchmarkDate);
31 | expect(maxCheck.max(beforeBenchmarkDate).maxDate).toEqual(
32 | beforeBenchmarkDate
33 | );
34 | });
35 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/date.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import * as z from "../index.ts";
6 |
7 | const beforeBenchmarkDate = new Date(2022, 10, 4);
8 | const benchmarkDate = new Date(2022, 10, 5);
9 | const afterBenchmarkDate = new Date(2022, 10, 6);
10 |
11 | const minCheck = z.date().min(benchmarkDate);
12 | const maxCheck = z.date().max(benchmarkDate);
13 |
14 | test("passing validations", () => {
15 | minCheck.parse(benchmarkDate);
16 | minCheck.parse(afterBenchmarkDate);
17 |
18 | maxCheck.parse(benchmarkDate);
19 | maxCheck.parse(beforeBenchmarkDate);
20 | });
21 |
22 | test("failing validations", () => {
23 | expect(() => minCheck.parse(beforeBenchmarkDate)).toThrow();
24 | expect(() => maxCheck.parse(afterBenchmarkDate)).toThrow();
25 | });
26 |
27 | test("min max getters", () => {
28 | expect(minCheck.minDate).toEqual(benchmarkDate);
29 | expect(minCheck.min(afterBenchmarkDate).minDate).toEqual(afterBenchmarkDate);
30 |
31 | expect(maxCheck.maxDate).toEqual(benchmarkDate);
32 | expect(maxCheck.max(beforeBenchmarkDate).maxDate).toEqual(
33 | beforeBenchmarkDate
34 | );
35 | });
36 |
--------------------------------------------------------------------------------
/src/__tests__/literal.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 |
6 | const literalTuna = z.literal("tuna");
7 | const literalFortyTwo = z.literal(42);
8 | const literalTrue = z.literal(true);
9 |
10 | const terrificSymbol = Symbol("terrific");
11 | const literalTerrificSymbol = z.literal(terrificSymbol);
12 |
13 | test("passing validations", () => {
14 | literalTuna.parse("tuna");
15 | literalFortyTwo.parse(42);
16 | literalTrue.parse(true);
17 | literalTerrificSymbol.parse(terrificSymbol);
18 | });
19 |
20 | test("failing validations", () => {
21 | expect(() => literalTuna.parse("shark")).toThrow();
22 | expect(() => literalFortyTwo.parse(43)).toThrow();
23 | expect(() => literalTrue.parse(false)).toThrow();
24 | expect(() => literalTerrificSymbol.parse(Symbol("terrific"))).toThrow();
25 | });
26 |
27 | test("invalid_literal should have `received` field with data", () => {
28 | const data = "shark";
29 | const result = literalTuna.safeParse(data);
30 | if (!result.success) {
31 | const issue = result.error.issues[0];
32 | if (issue.code === "invalid_literal") {
33 | expect(issue.received).toBe(data);
34 | }
35 | }
36 | });
37 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/literal.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import * as z from "../index.ts";
6 |
7 | const literalTuna = z.literal("tuna");
8 | const literalFortyTwo = z.literal(42);
9 | const literalTrue = z.literal(true);
10 |
11 | const terrificSymbol = Symbol("terrific");
12 | const literalTerrificSymbol = z.literal(terrificSymbol);
13 |
14 | test("passing validations", () => {
15 | literalTuna.parse("tuna");
16 | literalFortyTwo.parse(42);
17 | literalTrue.parse(true);
18 | literalTerrificSymbol.parse(terrificSymbol);
19 | });
20 |
21 | test("failing validations", () => {
22 | expect(() => literalTuna.parse("shark")).toThrow();
23 | expect(() => literalFortyTwo.parse(43)).toThrow();
24 | expect(() => literalTrue.parse(false)).toThrow();
25 | expect(() => literalTerrificSymbol.parse(Symbol("terrific"))).toThrow();
26 | });
27 |
28 | test("invalid_literal should have `received` field with data", () => {
29 | const data = "shark";
30 | const result = literalTuna.safeParse(data);
31 | if (!result.success) {
32 | const issue = result.error.issues[0];
33 | if (issue.code === "invalid_literal") {
34 | expect(issue.received).toBe(data);
35 | }
36 | }
37 | });
38 |
--------------------------------------------------------------------------------
/.github/workflows/release-canary.yml:
--------------------------------------------------------------------------------
1 | # .github/release.yml
2 |
3 | name: Release on npm (canary)
4 | on:
5 | push:
6 | branches:
7 | - "main"
8 | paths:
9 | - "src/**"
10 | - ".github/workflows/release-canary.yml"
11 | jobs:
12 | build_and_publish:
13 | runs-on: ubuntu-latest
14 | permissions:
15 | contents: write
16 | pull-requests: read
17 | id-token: write
18 | steps:
19 | - uses: actions/checkout@v4
20 | with:
21 | fetch-depth: 0
22 |
23 | - name: Set up Node
24 | uses: actions/setup-node@v4
25 | with:
26 | node-version: 20
27 |
28 | - name: Install dependencies
29 | run: |
30 | yarn install
31 |
32 | - name: Build
33 | run: |
34 | yarn clean
35 | yarn build
36 |
37 | - name: Set version
38 | run: |
39 | npm --no-git-tag-version version minor
40 | npm --no-git-tag-version version $(npm pkg get version | sed 's/"//g')-canary.$(date +'%Y%m%dT%H%M%S')
41 |
42 | - id: publish
43 | name: Publish to NPM
44 | uses: JS-DevTools/npm-publish@v3
45 | with:
46 | token: ${{ secrets.NPM_TOKEN }}
47 | dry-run: false
48 | tag: canary
49 | provenance: true
50 |
--------------------------------------------------------------------------------
/src/__tests__/instanceof.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import { util } from "../helpers/util";
5 | import * as z from "../index";
6 |
7 | test("instanceof", async () => {
8 | class Test {}
9 | class Subtest extends Test {}
10 | abstract class AbstractBar {
11 | constructor(public val: string) {}
12 | }
13 | class Bar extends AbstractBar {}
14 |
15 | const TestSchema = z.instanceof(Test);
16 | const SubtestSchema = z.instanceof(Subtest);
17 | const AbstractSchema = z.instanceof(AbstractBar);
18 | const BarSchema = z.instanceof(Bar);
19 |
20 | TestSchema.parse(new Test());
21 | TestSchema.parse(new Subtest());
22 | SubtestSchema.parse(new Subtest());
23 | AbstractSchema.parse(new Bar("asdf"));
24 | const bar = BarSchema.parse(new Bar("asdf"));
25 | expect(bar.val).toEqual("asdf");
26 |
27 | await expect(() => SubtestSchema.parse(new Test())).toThrow(
28 | /Input not instance of Subtest/
29 | );
30 | await expect(() => TestSchema.parse(12)).toThrow(
31 | /Input not instance of Test/
32 | );
33 |
34 | util.assertEqual>(true);
35 | });
36 |
37 | test("instanceof fatal", () => {
38 | const schema = z.instanceof(Date).refine((d) => d.toString());
39 | const res = schema.safeParse(null);
40 | expect(res.success).toBe(false);
41 | });
42 |
--------------------------------------------------------------------------------
/src/__tests__/nullable.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 |
6 | function checkErrors(a: z.ZodTypeAny, bad: any) {
7 | let expected;
8 | try {
9 | a.parse(bad);
10 | } catch (error) {
11 | expected = (error as z.ZodError).formErrors;
12 | }
13 | try {
14 | a.nullable().parse(bad);
15 | } catch (error) {
16 | expect((error as z.ZodError).formErrors).toEqual(expected);
17 | }
18 | }
19 |
20 | test("Should have error messages appropriate for the underlying type", () => {
21 | checkErrors(z.string().min(2), 1);
22 | z.string().min(2).nullable().parse(null);
23 | checkErrors(z.number().gte(2), 1);
24 | z.number().gte(2).nullable().parse(null);
25 | checkErrors(z.boolean(), "");
26 | z.boolean().nullable().parse(null);
27 | checkErrors(z.null(), null);
28 | z.null().nullable().parse(null);
29 | checkErrors(z.null(), {});
30 | z.null().nullable().parse(null);
31 | checkErrors(z.object({}), 1);
32 | z.object({}).nullable().parse(null);
33 | checkErrors(z.tuple([]), 1);
34 | z.tuple([]).nullable().parse(null);
35 | checkErrors(z.unknown(), 1);
36 | z.unknown().nullable().parse(null);
37 | });
38 |
39 | test("unwrap", () => {
40 | const unwrapped = z.string().nullable().unwrap();
41 | expect(unwrapped).toBeInstanceOf(z.ZodString);
42 | });
43 |
--------------------------------------------------------------------------------
/src/__tests__/parser.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 |
6 | test("parse strict object with unknown keys", () => {
7 | expect(() =>
8 | z
9 | .object({ name: z.string() })
10 | .strict()
11 | .parse({ name: "bill", unknownKey: 12 } as any)
12 | ).toThrow();
13 | });
14 |
15 | test("parse nonstrict object with unknown keys", () => {
16 | z.object({ name: z.string() })
17 | .nonstrict()
18 | .parse({ name: "bill", unknownKey: 12 });
19 | });
20 |
21 | test("invalid left side of intersection", () => {
22 | expect(() =>
23 | z.intersection(z.string(), z.number()).parse(12 as any)
24 | ).toThrow();
25 | });
26 |
27 | test("invalid right side of intersection", () => {
28 | expect(() =>
29 | z.intersection(z.string(), z.number()).parse("12" as any)
30 | ).toThrow();
31 | });
32 |
33 | test("parsing non-array in tuple schema", () => {
34 | expect(() => z.tuple([]).parse("12" as any)).toThrow();
35 | });
36 |
37 | test("incorrect num elements in tuple", () => {
38 | expect(() => z.tuple([]).parse(["asdf"] as any)).toThrow();
39 | });
40 |
41 | test("invalid enum value", () => {
42 | expect(() => z.enum(["Blue"]).parse("Red" as any)).toThrow();
43 | });
44 |
45 | test("parsing unknown", () => {
46 | z.string().parse("Red" as unknown);
47 | });
48 |
--------------------------------------------------------------------------------
/src/__tests__/optional.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 |
6 | function checkErrors(a: z.ZodTypeAny, bad: any) {
7 | let expected;
8 | try {
9 | a.parse(bad);
10 | } catch (error) {
11 | expected = (error as z.ZodError).formErrors;
12 | }
13 | try {
14 | a.optional().parse(bad);
15 | } catch (error) {
16 | expect((error as z.ZodError).formErrors).toEqual(expected);
17 | }
18 | }
19 |
20 | test("Should have error messages appropriate for the underlying type", () => {
21 | checkErrors(z.string().min(2), 1);
22 | z.string().min(2).optional().parse(undefined);
23 | checkErrors(z.number().gte(2), 1);
24 | z.number().gte(2).optional().parse(undefined);
25 | checkErrors(z.boolean(), "");
26 | z.boolean().optional().parse(undefined);
27 | checkErrors(z.undefined(), null);
28 | z.undefined().optional().parse(undefined);
29 | checkErrors(z.null(), {});
30 | z.null().optional().parse(undefined);
31 | checkErrors(z.object({}), 1);
32 | z.object({}).optional().parse(undefined);
33 | checkErrors(z.tuple([]), 1);
34 | z.tuple([]).optional().parse(undefined);
35 | checkErrors(z.unknown(), 1);
36 | z.unknown().optional().parse(undefined);
37 | });
38 |
39 | test("unwrap", () => {
40 | const unwrapped = z.string().optional().unwrap();
41 | expect(unwrapped).toBeInstanceOf(z.ZodString);
42 | });
43 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/instanceof.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import { util } from "../helpers/util.ts";
6 | import * as z from "../index.ts";
7 |
8 | test("instanceof", async () => {
9 | class Test {}
10 | class Subtest extends Test {}
11 | abstract class AbstractBar {
12 | constructor(public val: string) {}
13 | }
14 | class Bar extends AbstractBar {}
15 |
16 | const TestSchema = z.instanceof(Test);
17 | const SubtestSchema = z.instanceof(Subtest);
18 | const AbstractSchema = z.instanceof(AbstractBar);
19 | const BarSchema = z.instanceof(Bar);
20 |
21 | TestSchema.parse(new Test());
22 | TestSchema.parse(new Subtest());
23 | SubtestSchema.parse(new Subtest());
24 | AbstractSchema.parse(new Bar("asdf"));
25 | const bar = BarSchema.parse(new Bar("asdf"));
26 | expect(bar.val).toEqual("asdf");
27 |
28 | await expect(() => SubtestSchema.parse(new Test())).toThrow(
29 | /Input not instance of Subtest/
30 | );
31 | await expect(() => TestSchema.parse(12)).toThrow(
32 | /Input not instance of Test/
33 | );
34 |
35 | util.assertEqual>(true);
36 | });
37 |
38 | test("instanceof fatal", () => {
39 | const schema = z.instanceof(Date).refine((d) => d.toString());
40 | const res = schema.safeParse(null);
41 | expect(res.success).toBe(false);
42 | });
43 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/nullable.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import * as z from "../index.ts";
6 |
7 | function checkErrors(a: z.ZodTypeAny, bad: any) {
8 | let expected;
9 | try {
10 | a.parse(bad);
11 | } catch (error) {
12 | expected = (error as z.ZodError).formErrors;
13 | }
14 | try {
15 | a.nullable().parse(bad);
16 | } catch (error) {
17 | expect((error as z.ZodError).formErrors).toEqual(expected);
18 | }
19 | }
20 |
21 | test("Should have error messages appropriate for the underlying type", () => {
22 | checkErrors(z.string().min(2), 1);
23 | z.string().min(2).nullable().parse(null);
24 | checkErrors(z.number().gte(2), 1);
25 | z.number().gte(2).nullable().parse(null);
26 | checkErrors(z.boolean(), "");
27 | z.boolean().nullable().parse(null);
28 | checkErrors(z.null(), null);
29 | z.null().nullable().parse(null);
30 | checkErrors(z.null(), {});
31 | z.null().nullable().parse(null);
32 | checkErrors(z.object({}), 1);
33 | z.object({}).nullable().parse(null);
34 | checkErrors(z.tuple([]), 1);
35 | z.tuple([]).nullable().parse(null);
36 | checkErrors(z.unknown(), 1);
37 | z.unknown().nullable().parse(null);
38 | });
39 |
40 | test("unwrap", () => {
41 | const unwrapped = z.string().nullable().unwrap();
42 | expect(unwrapped).toBeInstanceOf(z.ZodString);
43 | });
44 |
--------------------------------------------------------------------------------
/src/__tests__/Mocker.ts:
--------------------------------------------------------------------------------
1 | function getRandomInt(max: number) {
2 | return Math.floor(Math.random() * Math.floor(max));
3 | }
4 |
5 | const testSymbol = Symbol("test");
6 |
7 | export class Mocker {
8 | pick = (...args: any[]) => {
9 | return args[getRandomInt(args.length)];
10 | };
11 |
12 | get string() {
13 | return Math.random().toString(36).substring(7);
14 | }
15 | get number() {
16 | return Math.random() * 100;
17 | }
18 | get bigint() {
19 | return BigInt(Math.floor(Math.random() * 10000));
20 | }
21 | get boolean() {
22 | return Math.random() < 0.5;
23 | }
24 | get date() {
25 | return new Date(Math.floor(Date.now() * Math.random()));
26 | }
27 | get symbol() {
28 | return testSymbol;
29 | }
30 | get null(): null {
31 | return null;
32 | }
33 | get undefined(): undefined {
34 | return undefined;
35 | }
36 | get stringOptional() {
37 | return this.pick(this.string, this.undefined);
38 | }
39 | get stringNullable() {
40 | return this.pick(this.string, this.null);
41 | }
42 | get numberOptional() {
43 | return this.pick(this.number, this.undefined);
44 | }
45 | get numberNullable() {
46 | return this.pick(this.number, this.null);
47 | }
48 | get booleanOptional() {
49 | return this.pick(this.boolean, this.undefined);
50 | }
51 | get booleanNullable() {
52 | return this.pick(this.boolean, this.null);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/Mocker.ts:
--------------------------------------------------------------------------------
1 | function getRandomInt(max: number) {
2 | return Math.floor(Math.random() * Math.floor(max));
3 | }
4 |
5 | const testSymbol = Symbol("test");
6 |
7 | export class Mocker {
8 | pick = (...args: any[]) => {
9 | return args[getRandomInt(args.length)];
10 | };
11 |
12 | get string() {
13 | return Math.random().toString(36).substring(7);
14 | }
15 | get number() {
16 | return Math.random() * 100;
17 | }
18 | get bigint() {
19 | return BigInt(Math.floor(Math.random() * 10000));
20 | }
21 | get boolean() {
22 | return Math.random() < 0.5;
23 | }
24 | get date() {
25 | return new Date(Math.floor(Date.now() * Math.random()));
26 | }
27 | get symbol() {
28 | return testSymbol;
29 | }
30 | get null(): null {
31 | return null;
32 | }
33 | get undefined(): undefined {
34 | return undefined;
35 | }
36 | get stringOptional() {
37 | return this.pick(this.string, this.undefined);
38 | }
39 | get stringNullable() {
40 | return this.pick(this.string, this.null);
41 | }
42 | get numberOptional() {
43 | return this.pick(this.number, this.undefined);
44 | }
45 | get numberNullable() {
46 | return this.pick(this.number, this.null);
47 | }
48 | get booleanOptional() {
49 | return this.pick(this.boolean, this.undefined);
50 | }
51 | get booleanNullable() {
52 | return this.pick(this.boolean, this.null);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/parser.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import * as z from "../index.ts";
6 |
7 | test("parse strict object with unknown keys", () => {
8 | expect(() =>
9 | z
10 | .object({ name: z.string() })
11 | .strict()
12 | .parse({ name: "bill", unknownKey: 12 } as any)
13 | ).toThrow();
14 | });
15 |
16 | test("parse nonstrict object with unknown keys", () => {
17 | z.object({ name: z.string() })
18 | .nonstrict()
19 | .parse({ name: "bill", unknownKey: 12 });
20 | });
21 |
22 | test("invalid left side of intersection", () => {
23 | expect(() =>
24 | z.intersection(z.string(), z.number()).parse(12 as any)
25 | ).toThrow();
26 | });
27 |
28 | test("invalid right side of intersection", () => {
29 | expect(() =>
30 | z.intersection(z.string(), z.number()).parse("12" as any)
31 | ).toThrow();
32 | });
33 |
34 | test("parsing non-array in tuple schema", () => {
35 | expect(() => z.tuple([]).parse("12" as any)).toThrow();
36 | });
37 |
38 | test("incorrect num elements in tuple", () => {
39 | expect(() => z.tuple([]).parse(["asdf"] as any)).toThrow();
40 | });
41 |
42 | test("invalid enum value", () => {
43 | expect(() => z.enum(["Blue"]).parse("Red" as any)).toThrow();
44 | });
45 |
46 | test("parsing unknown", () => {
47 | z.string().parse("Red" as unknown);
48 | });
49 |
--------------------------------------------------------------------------------
/src/benchmarks/realworld.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | import { z } from "../index";
4 |
5 | const shortSuite = new Benchmark.Suite("realworld");
6 |
7 | const People = z.array(
8 | z.object({
9 | type: z.literal("person"),
10 | hair: z.enum(["blue", "brown"]),
11 | active: z.boolean(),
12 | name: z.string(),
13 | age: z.number().int(),
14 | hobbies: z.array(z.string()),
15 | address: z.object({
16 | street: z.string(),
17 | zip: z.string(),
18 | country: z.string(),
19 | }),
20 | })
21 | );
22 |
23 | let i = 0;
24 |
25 | function num() {
26 | return ++i;
27 | }
28 |
29 | function str() {
30 | return (++i % 100).toString(16);
31 | }
32 |
33 | function array(fn: () => T): T[] {
34 | return Array.from({ length: ++i % 10 }, () => fn());
35 | }
36 |
37 | const people = Array.from({ length: 100 }, () => {
38 | return {
39 | type: "person",
40 | hair: i % 2 ? "blue" : "brown",
41 | active: !!(i % 2),
42 | name: str(),
43 | age: num(),
44 | hobbies: array(str),
45 | address: {
46 | street: str(),
47 | zip: str(),
48 | country: str(),
49 | },
50 | };
51 | });
52 |
53 | shortSuite
54 | .add("valid", () => {
55 | People.parse(people);
56 | })
57 | .on("cycle", (e: Benchmark.Event) => {
58 | console.log(`${(shortSuite as any).name}: ${e.target}`);
59 | });
60 |
61 | export default {
62 | suites: [shortSuite],
63 | };
64 |
--------------------------------------------------------------------------------
/deno/lib/benchmarks/realworld.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | import { z } from "../index.ts";
4 |
5 | const shortSuite = new Benchmark.Suite("realworld");
6 |
7 | const People = z.array(
8 | z.object({
9 | type: z.literal("person"),
10 | hair: z.enum(["blue", "brown"]),
11 | active: z.boolean(),
12 | name: z.string(),
13 | age: z.number().int(),
14 | hobbies: z.array(z.string()),
15 | address: z.object({
16 | street: z.string(),
17 | zip: z.string(),
18 | country: z.string(),
19 | }),
20 | })
21 | );
22 |
23 | let i = 0;
24 |
25 | function num() {
26 | return ++i;
27 | }
28 |
29 | function str() {
30 | return (++i % 100).toString(16);
31 | }
32 |
33 | function array(fn: () => T): T[] {
34 | return Array.from({ length: ++i % 10 }, () => fn());
35 | }
36 |
37 | const people = Array.from({ length: 100 }, () => {
38 | return {
39 | type: "person",
40 | hair: i % 2 ? "blue" : "brown",
41 | active: !!(i % 2),
42 | name: str(),
43 | age: num(),
44 | hobbies: array(str),
45 | address: {
46 | street: str(),
47 | zip: str(),
48 | country: str(),
49 | },
50 | };
51 | });
52 |
53 | shortSuite
54 | .add("valid", () => {
55 | People.parse(people);
56 | })
57 | .on("cycle", (e: Benchmark.Event) => {
58 | console.log(`${(shortSuite as any).name}: ${e.target}`);
59 | });
60 |
61 | export default {
62 | suites: [shortSuite],
63 | };
64 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/optional.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import * as z from "../index.ts";
6 |
7 | function checkErrors(a: z.ZodTypeAny, bad: any) {
8 | let expected;
9 | try {
10 | a.parse(bad);
11 | } catch (error) {
12 | expected = (error as z.ZodError).formErrors;
13 | }
14 | try {
15 | a.optional().parse(bad);
16 | } catch (error) {
17 | expect((error as z.ZodError).formErrors).toEqual(expected);
18 | }
19 | }
20 |
21 | test("Should have error messages appropriate for the underlying type", () => {
22 | checkErrors(z.string().min(2), 1);
23 | z.string().min(2).optional().parse(undefined);
24 | checkErrors(z.number().gte(2), 1);
25 | z.number().gte(2).optional().parse(undefined);
26 | checkErrors(z.boolean(), "");
27 | z.boolean().optional().parse(undefined);
28 | checkErrors(z.undefined(), null);
29 | z.undefined().optional().parse(undefined);
30 | checkErrors(z.null(), {});
31 | z.null().optional().parse(undefined);
32 | checkErrors(z.object({}), 1);
33 | z.object({}).optional().parse(undefined);
34 | checkErrors(z.tuple([]), 1);
35 | z.tuple([]).optional().parse(undefined);
36 | checkErrors(z.unknown(), 1);
37 | z.unknown().optional().parse(undefined);
38 | });
39 |
40 | test("unwrap", () => {
41 | const unwrapped = z.string().optional().unwrap();
42 | expect(unwrapped).toBeInstanceOf(z.ZodString);
43 | });
44 |
--------------------------------------------------------------------------------
/src/__tests__/description.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 |
6 | const description = "a description";
7 |
8 | test("passing `description` to schema should add a description", () => {
9 | expect(z.string({ description }).description).toEqual(description);
10 | expect(z.number({ description }).description).toEqual(description);
11 | expect(z.boolean({ description }).description).toEqual(description);
12 | });
13 |
14 | test("`.describe` should add a description", () => {
15 | expect(z.string().describe(description).description).toEqual(description);
16 | expect(z.number().describe(description).description).toEqual(description);
17 | expect(z.boolean().describe(description).description).toEqual(description);
18 | });
19 |
20 | test("description should carry over to chained schemas", () => {
21 | const schema = z.string({ description });
22 | expect(schema.description).toEqual(description);
23 | expect(schema.optional().description).toEqual(description);
24 | expect(schema.optional().nullable().default("default").description).toEqual(
25 | description
26 | );
27 | });
28 |
29 | test("description should not carry over to chained array schema", () => {
30 | const schema = z.string().describe(description);
31 |
32 | expect(schema.description).toEqual(description);
33 | expect(schema.array().description).toEqual(undefined);
34 | expect(z.array(schema).description).toEqual(undefined);
35 | });
36 |
--------------------------------------------------------------------------------
/src/benchmarks/string.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | import { z } from "../index";
4 |
5 | const SUITE_NAME = "z.string";
6 | const suite = new Benchmark.Suite(SUITE_NAME);
7 |
8 | const empty = "";
9 | const short = "short";
10 | const long = "long".repeat(256);
11 | const manual = (str: unknown) => {
12 | if (typeof str !== "string") {
13 | throw new Error("Not a string");
14 | }
15 |
16 | return str;
17 | };
18 | const stringSchema = z.string();
19 | const optionalStringSchema = z.string().optional();
20 | const optionalNullableStringSchema = z.string().optional().nullable();
21 |
22 | suite
23 | .add("empty string", () => {
24 | stringSchema.parse(empty);
25 | })
26 | .add("short string", () => {
27 | stringSchema.parse(short);
28 | })
29 | .add("long string", () => {
30 | stringSchema.parse(long);
31 | })
32 | .add("optional string", () => {
33 | optionalStringSchema.parse(long);
34 | })
35 | .add("nullable string", () => {
36 | optionalNullableStringSchema.parse(long);
37 | })
38 | .add("nullable (null) string", () => {
39 | optionalNullableStringSchema.parse(null);
40 | })
41 | .add("invalid: null", () => {
42 | try {
43 | stringSchema.parse(null);
44 | } catch (err) {}
45 | })
46 | .add("manual parser: long", () => {
47 | manual(long);
48 | })
49 | .on("cycle", (e: Benchmark.Event) => {
50 | console.log(`${SUITE_NAME}: ${e.target}`);
51 | });
52 |
53 | export default {
54 | suites: [suite],
55 | };
56 |
--------------------------------------------------------------------------------
/deno/lib/benchmarks/string.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | import { z } from "../index.ts";
4 |
5 | const SUITE_NAME = "z.string";
6 | const suite = new Benchmark.Suite(SUITE_NAME);
7 |
8 | const empty = "";
9 | const short = "short";
10 | const long = "long".repeat(256);
11 | const manual = (str: unknown) => {
12 | if (typeof str !== "string") {
13 | throw new Error("Not a string");
14 | }
15 |
16 | return str;
17 | };
18 | const stringSchema = z.string();
19 | const optionalStringSchema = z.string().optional();
20 | const optionalNullableStringSchema = z.string().optional().nullable();
21 |
22 | suite
23 | .add("empty string", () => {
24 | stringSchema.parse(empty);
25 | })
26 | .add("short string", () => {
27 | stringSchema.parse(short);
28 | })
29 | .add("long string", () => {
30 | stringSchema.parse(long);
31 | })
32 | .add("optional string", () => {
33 | optionalStringSchema.parse(long);
34 | })
35 | .add("nullable string", () => {
36 | optionalNullableStringSchema.parse(long);
37 | })
38 | .add("nullable (null) string", () => {
39 | optionalNullableStringSchema.parse(null);
40 | })
41 | .add("invalid: null", () => {
42 | try {
43 | stringSchema.parse(null);
44 | } catch (err) {}
45 | })
46 | .add("manual parser: long", () => {
47 | manual(long);
48 | })
49 | .on("cycle", (e: Benchmark.Event) => {
50 | console.log(`${SUITE_NAME}: ${e.target}`);
51 | });
52 |
53 | export default {
54 | suites: [suite],
55 | };
56 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/description.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import * as z from "../index.ts";
6 |
7 | const description = "a description";
8 |
9 | test("passing `description` to schema should add a description", () => {
10 | expect(z.string({ description }).description).toEqual(description);
11 | expect(z.number({ description }).description).toEqual(description);
12 | expect(z.boolean({ description }).description).toEqual(description);
13 | });
14 |
15 | test("`.describe` should add a description", () => {
16 | expect(z.string().describe(description).description).toEqual(description);
17 | expect(z.number().describe(description).description).toEqual(description);
18 | expect(z.boolean().describe(description).description).toEqual(description);
19 | });
20 |
21 | test("description should carry over to chained schemas", () => {
22 | const schema = z.string({ description });
23 | expect(schema.description).toEqual(description);
24 | expect(schema.optional().description).toEqual(description);
25 | expect(schema.optional().nullable().default("default").description).toEqual(
26 | description
27 | );
28 | });
29 |
30 | test("description should not carry over to chained array schema", () => {
31 | const schema = z.string().describe(description);
32 |
33 | expect(schema.description).toEqual(description);
34 | expect(schema.array().description).toEqual(undefined);
35 | expect(z.array(schema).description).toEqual(undefined);
36 | });
37 |
--------------------------------------------------------------------------------
/src/__tests__/crazySchema.ts:
--------------------------------------------------------------------------------
1 | import * as z from "../index";
2 |
3 | export const crazySchema = z.object({
4 | tuple: z.tuple([
5 | z.string().nullable().optional(),
6 | z.number().nullable().optional(),
7 | z.boolean().nullable().optional(),
8 | z.null().nullable().optional(),
9 | z.undefined().nullable().optional(),
10 | z.literal("1234").nullable().optional(),
11 | ]),
12 | merged: z
13 | .object({
14 | k1: z.string().optional(),
15 | })
16 | .merge(z.object({ k1: z.string().nullable(), k2: z.number() })),
17 | union: z.array(z.union([z.literal("asdf"), z.literal(12)])).nonempty(),
18 | array: z.array(z.number()),
19 | // sumTransformer: z.transformer(z.array(z.number()), z.number(), (arg) => {
20 | // return arg.reduce((a, b) => a + b, 0);
21 | // }),
22 | sumMinLength: z.array(z.number()).refine((arg) => arg.length > 5),
23 | intersection: z.intersection(
24 | z.object({ p1: z.string().optional() }),
25 | z.object({ p1: z.number().optional() })
26 | ),
27 | enum: z.intersection(z.enum(["zero", "one"]), z.enum(["one", "two"])),
28 | nonstrict: z.object({ points: z.number() }).nonstrict(),
29 | numProm: z.promise(z.number()),
30 | lenfun: z.function(z.tuple([z.string()]), z.boolean()),
31 | });
32 |
33 | export const asyncCrazySchema = crazySchema.extend({
34 | // async_transform: z.transformer(
35 | // z.array(z.number()),
36 | // z.number(),
37 | // async (arg) => {
38 | // return arg.reduce((a, b) => a + b, 0);
39 | // }
40 | // ),
41 | async_refine: z.array(z.number()).refine(async (arg) => arg.length > 5),
42 | });
43 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/crazySchema.ts:
--------------------------------------------------------------------------------
1 | import * as z from "../index.ts";
2 |
3 | export const crazySchema = z.object({
4 | tuple: z.tuple([
5 | z.string().nullable().optional(),
6 | z.number().nullable().optional(),
7 | z.boolean().nullable().optional(),
8 | z.null().nullable().optional(),
9 | z.undefined().nullable().optional(),
10 | z.literal("1234").nullable().optional(),
11 | ]),
12 | merged: z
13 | .object({
14 | k1: z.string().optional(),
15 | })
16 | .merge(z.object({ k1: z.string().nullable(), k2: z.number() })),
17 | union: z.array(z.union([z.literal("asdf"), z.literal(12)])).nonempty(),
18 | array: z.array(z.number()),
19 | // sumTransformer: z.transformer(z.array(z.number()), z.number(), (arg) => {
20 | // return arg.reduce((a, b) => a + b, 0);
21 | // }),
22 | sumMinLength: z.array(z.number()).refine((arg) => arg.length > 5),
23 | intersection: z.intersection(
24 | z.object({ p1: z.string().optional() }),
25 | z.object({ p1: z.number().optional() })
26 | ),
27 | enum: z.intersection(z.enum(["zero", "one"]), z.enum(["one", "two"])),
28 | nonstrict: z.object({ points: z.number() }).nonstrict(),
29 | numProm: z.promise(z.number()),
30 | lenfun: z.function(z.tuple([z.string()]), z.boolean()),
31 | });
32 |
33 | export const asyncCrazySchema = crazySchema.extend({
34 | // async_transform: z.transformer(
35 | // z.array(z.number()),
36 | // z.number(),
37 | // async (arg) => {
38 | // return arg.reduce((a, b) => a + b, 0);
39 | // }
40 | // ),
41 | async_refine: z.array(z.number()).refine(async (arg) => arg.length > 5),
42 | });
43 |
--------------------------------------------------------------------------------
/src/__tests__/language-server.source.ts:
--------------------------------------------------------------------------------
1 | import * as z from "../index";
2 |
3 | export const filePath = __filename;
4 |
5 | // z.object()
6 |
7 | export const Test = z.object({
8 | f1: z.number(),
9 | });
10 |
11 | export type Test = z.infer;
12 |
13 | export const instanceOfTest: Test = {
14 | f1: 1,
15 | };
16 |
17 | // z.object().merge()
18 |
19 | export const TestMerge = z
20 | .object({
21 | f2: z.string().optional(),
22 | })
23 | .merge(Test);
24 |
25 | export type TestMerge = z.infer;
26 |
27 | export const instanceOfTestMerge: TestMerge = {
28 | f1: 1,
29 | f2: "string",
30 | };
31 |
32 | // z.union()
33 |
34 | export const TestUnion = z.union([
35 | z.object({
36 | f2: z.string().optional(),
37 | }),
38 | Test,
39 | ]);
40 |
41 | export type TestUnion = z.infer;
42 |
43 | export const instanceOfTestUnion: TestUnion = {
44 | f1: 1,
45 | f2: "string",
46 | };
47 |
48 | // z.object().partial()
49 |
50 | export const TestPartial = Test.partial();
51 |
52 | export type TestPartial = z.infer;
53 |
54 | export const instanceOfTestPartial: TestPartial = {
55 | f1: 1,
56 | };
57 |
58 | // z.object().pick()
59 |
60 | export const TestPick = TestMerge.pick({ f1: true });
61 |
62 | export type TestPick = z.infer;
63 |
64 | export const instanceOfTestPick: TestPick = {
65 | f1: 1,
66 | };
67 |
68 | // z.object().omit()
69 |
70 | export const TestOmit = TestMerge.omit({ f2: true });
71 |
72 | export type TestOmit = z.infer;
73 |
74 | export const instanceOfTestOmit: TestOmit = {
75 | f1: 1,
76 | };
77 |
--------------------------------------------------------------------------------
/src/__tests__/generics.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import { util } from "../helpers/util";
5 | import * as z from "../index";
6 |
7 | test("generics", () => {
8 | async function stripOuter(
9 | schema: TData,
10 | data: unknown
11 | ) {
12 | return z
13 | .object({
14 | nested: schema, // as z.ZodTypeAny,
15 | })
16 | .transform((data) => {
17 | return data.nested!;
18 | })
19 | .parse({ nested: data });
20 | }
21 |
22 | const result = stripOuter(z.object({ a: z.string() }), { a: "asdf" });
23 | util.assertEqual>(true);
24 | });
25 |
26 | // test("assignability", () => {
27 | // const createSchemaAndParse = (
28 | // key: K,
29 | // valueSchema: VS,
30 | // data: unknown
31 | // ) => {
32 | // const schema = z.object({
33 | // [key]: valueSchema,
34 | // } as { [k in K]: VS });
35 | // return { [key]: valueSchema };
36 | // const parsed = schema.parse(data);
37 | // return parsed;
38 | // // const inferred: z.infer> = parsed;
39 | // // return inferred;
40 | // };
41 | // const parsed = createSchemaAndParse("foo", z.string(), { foo: "" });
42 | // util.assertEqual(true);
43 | // });
44 |
45 | test("nested no undefined", () => {
46 | const inner = z.string().or(z.array(z.string()));
47 | const outer = z.object({ inner });
48 | type outerSchema = z.infer;
49 | z.util.assertEqual(true);
50 | expect(outer.safeParse({ inner: undefined }).success).toEqual(false);
51 | });
52 |
--------------------------------------------------------------------------------
/src/benchmarks/index.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | import datetimeBenchmarks from "./datetime";
4 | import discriminatedUnionBenchmarks from "./discriminatedUnion";
5 | import ipv4Benchmarks from "./ipv4";
6 | import objectBenchmarks from "./object";
7 | import primitiveBenchmarks from "./primitives";
8 | import realworld from "./realworld";
9 | import stringBenchmarks from "./string";
10 | import unionBenchmarks from "./union";
11 |
12 | const argv = process.argv.slice(2);
13 | let suites: Benchmark.Suite[] = [];
14 |
15 | if (!argv.length) {
16 | suites = [
17 | ...realworld.suites,
18 | ...primitiveBenchmarks.suites,
19 | ...stringBenchmarks.suites,
20 | ...objectBenchmarks.suites,
21 | ...unionBenchmarks.suites,
22 | ...discriminatedUnionBenchmarks.suites,
23 | ];
24 | } else {
25 | if (argv.includes("--realworld")) {
26 | suites.push(...realworld.suites);
27 | }
28 | if (argv.includes("--primitives")) {
29 | suites.push(...primitiveBenchmarks.suites);
30 | }
31 | if (argv.includes("--string")) {
32 | suites.push(...stringBenchmarks.suites);
33 | }
34 | if (argv.includes("--object")) {
35 | suites.push(...objectBenchmarks.suites);
36 | }
37 | if (argv.includes("--union")) {
38 | suites.push(...unionBenchmarks.suites);
39 | }
40 | if (argv.includes("--discriminatedUnion")) {
41 | suites.push(...datetimeBenchmarks.suites);
42 | }
43 | if (argv.includes("--datetime")) {
44 | suites.push(...datetimeBenchmarks.suites);
45 | }
46 | if (argv.includes("--ipv4")) {
47 | suites.push(...ipv4Benchmarks.suites);
48 | }
49 | }
50 |
51 | for (const suite of suites) {
52 | suite.run({});
53 | }
54 |
55 | // exit on Ctrl-C
56 | process.on("SIGINT", function () {
57 | console.log("Exiting...");
58 | process.exit();
59 | });
60 |
--------------------------------------------------------------------------------
/src/__tests__/async-refinements.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 |
6 | test("parse async test", async () => {
7 | const schema1 = z.string().refine(async (_val) => false);
8 | expect(() => schema1.parse("asdf")).toThrow();
9 |
10 | const schema2 = z.string().refine((_val) => Promise.resolve(true));
11 | return await expect(() => schema2.parse("asdf")).toThrow();
12 | });
13 |
14 | test("parseAsync async test", async () => {
15 | const schema1 = z.string().refine(async (_val) => true);
16 | await schema1.parseAsync("asdf");
17 |
18 | const schema2 = z.string().refine(async (_val) => false);
19 | return await expect(schema2.parseAsync("asdf")).rejects.toBeDefined();
20 | // expect(async () => await schema2.parseAsync('asdf')).toThrow();
21 | });
22 |
23 | test("parseAsync async test", async () => {
24 | // expect.assertions(2);
25 |
26 | const schema1 = z.string().refine((_val) => Promise.resolve(true));
27 | const v1 = await schema1.parseAsync("asdf");
28 | expect(v1).toEqual("asdf");
29 |
30 | const schema2 = z.string().refine((_val) => Promise.resolve(false));
31 | await expect(schema2.parseAsync("asdf")).rejects.toBeDefined();
32 |
33 | const schema3 = z.string().refine((_val) => Promise.resolve(true));
34 | await expect(schema3.parseAsync("asdf")).resolves.toEqual("asdf");
35 | return await expect(schema3.parseAsync("qwer")).resolves.toEqual("qwer");
36 | });
37 |
38 | test("parseAsync async with value", async () => {
39 | const schema1 = z.string().refine(async (val) => {
40 | return val.length > 5;
41 | });
42 | await expect(schema1.parseAsync("asdf")).rejects.toBeDefined();
43 |
44 | const v = await schema1.parseAsync("asdf123");
45 | return await expect(v).toEqual("asdf123");
46 | });
47 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/generics.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import { util } from "../helpers/util.ts";
6 | import * as z from "../index.ts";
7 |
8 | test("generics", () => {
9 | async function stripOuter(
10 | schema: TData,
11 | data: unknown
12 | ) {
13 | return z
14 | .object({
15 | nested: schema, // as z.ZodTypeAny,
16 | })
17 | .transform((data) => {
18 | return data.nested!;
19 | })
20 | .parse({ nested: data });
21 | }
22 |
23 | const result = stripOuter(z.object({ a: z.string() }), { a: "asdf" });
24 | util.assertEqual>(true);
25 | });
26 |
27 | // test("assignability", () => {
28 | // const createSchemaAndParse = (
29 | // key: K,
30 | // valueSchema: VS,
31 | // data: unknown
32 | // ) => {
33 | // const schema = z.object({
34 | // [key]: valueSchema,
35 | // } as { [k in K]: VS });
36 | // return { [key]: valueSchema };
37 | // const parsed = schema.parse(data);
38 | // return parsed;
39 | // // const inferred: z.infer> = parsed;
40 | // // return inferred;
41 | // };
42 | // const parsed = createSchemaAndParse("foo", z.string(), { foo: "" });
43 | // util.assertEqual(true);
44 | // });
45 |
46 | test("nested no undefined", () => {
47 | const inner = z.string().or(z.array(z.string()));
48 | const outer = z.object({ inner });
49 | type outerSchema = z.infer;
50 | z.util.assertEqual(true);
51 | expect(outer.safeParse({ inner: undefined }).success).toEqual(false);
52 | });
53 |
--------------------------------------------------------------------------------
/deno/lib/benchmarks/index.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | import datetimeBenchmarks from "./datetime.ts";
4 | import discriminatedUnionBenchmarks from "./discriminatedUnion.ts";
5 | import ipv4Benchmarks from "./ipv4.ts";
6 | import objectBenchmarks from "./object.ts";
7 | import primitiveBenchmarks from "./primitives.ts";
8 | import realworld from "./realworld.ts";
9 | import stringBenchmarks from "./string.ts";
10 | import unionBenchmarks from "./union.ts";
11 |
12 | const argv = process.argv.slice(2);
13 | let suites: Benchmark.Suite[] = [];
14 |
15 | if (!argv.length) {
16 | suites = [
17 | ...realworld.suites,
18 | ...primitiveBenchmarks.suites,
19 | ...stringBenchmarks.suites,
20 | ...objectBenchmarks.suites,
21 | ...unionBenchmarks.suites,
22 | ...discriminatedUnionBenchmarks.suites,
23 | ];
24 | } else {
25 | if (argv.includes("--realworld")) {
26 | suites.push(...realworld.suites);
27 | }
28 | if (argv.includes("--primitives")) {
29 | suites.push(...primitiveBenchmarks.suites);
30 | }
31 | if (argv.includes("--string")) {
32 | suites.push(...stringBenchmarks.suites);
33 | }
34 | if (argv.includes("--object")) {
35 | suites.push(...objectBenchmarks.suites);
36 | }
37 | if (argv.includes("--union")) {
38 | suites.push(...unionBenchmarks.suites);
39 | }
40 | if (argv.includes("--discriminatedUnion")) {
41 | suites.push(...datetimeBenchmarks.suites);
42 | }
43 | if (argv.includes("--datetime")) {
44 | suites.push(...datetimeBenchmarks.suites);
45 | }
46 | if (argv.includes("--ipv4")) {
47 | suites.push(...ipv4Benchmarks.suites);
48 | }
49 | }
50 |
51 | for (const suite of suites) {
52 | suite.run({});
53 | }
54 |
55 | // exit on Ctrl-C
56 | process.on("SIGINT", function () {
57 | console.log("Exiting...");
58 | process.exit();
59 | });
60 |
--------------------------------------------------------------------------------
/src/__tests__/unions.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 |
6 | test("function parsing", () => {
7 | const schema = z.union([
8 | z.string().refine(() => false),
9 | z.number().refine(() => false),
10 | ]);
11 | const result = schema.safeParse("asdf");
12 | expect(result.success).toEqual(false);
13 | });
14 |
15 | test("union 2", () => {
16 | const result = z
17 | .union([z.number(), z.string().refine(() => false)])
18 | .safeParse("a");
19 | expect(result.success).toEqual(false);
20 | });
21 |
22 | test("return valid over invalid", () => {
23 | const schema = z.union([
24 | z.object({
25 | email: z.string().email(),
26 | }),
27 | z.string(),
28 | ]);
29 | expect(schema.parse("asdf")).toEqual("asdf");
30 | expect(schema.parse({ email: "asdlkjf@lkajsdf.com" })).toEqual({
31 | email: "asdlkjf@lkajsdf.com",
32 | });
33 | });
34 |
35 | test("return dirty result over aborted", () => {
36 | const result = z
37 | .union([z.number(), z.string().refine(() => false)])
38 | .safeParse("a");
39 | expect(result.success).toEqual(false);
40 | if (!result.success) {
41 | expect(result.error.issues).toEqual([
42 | {
43 | code: "custom",
44 | message: "Invalid input",
45 | path: [],
46 | },
47 | ]);
48 | }
49 | });
50 |
51 | test("options getter", async () => {
52 | const union = z.union([z.string(), z.number()]);
53 | union.options[0].parse("asdf");
54 | union.options[1].parse(1234);
55 | await union.options[0].parseAsync("asdf");
56 | await union.options[1].parseAsync(1234);
57 | });
58 |
59 | test("readonly union", async () => {
60 | const options = [z.string(), z.number()] as const;
61 | const union = z.union(options);
62 | union.parse("asdf");
63 | union.parse(12);
64 | });
65 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/async-refinements.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import * as z from "../index.ts";
6 |
7 | test("parse async test", async () => {
8 | const schema1 = z.string().refine(async (_val) => false);
9 | expect(() => schema1.parse("asdf")).toThrow();
10 |
11 | const schema2 = z.string().refine((_val) => Promise.resolve(true));
12 | return await expect(() => schema2.parse("asdf")).toThrow();
13 | });
14 |
15 | test("parseAsync async test", async () => {
16 | const schema1 = z.string().refine(async (_val) => true);
17 | await schema1.parseAsync("asdf");
18 |
19 | const schema2 = z.string().refine(async (_val) => false);
20 | return await expect(schema2.parseAsync("asdf")).rejects.toBeDefined();
21 | // expect(async () => await schema2.parseAsync('asdf')).toThrow();
22 | });
23 |
24 | test("parseAsync async test", async () => {
25 | // expect.assertions(2);
26 |
27 | const schema1 = z.string().refine((_val) => Promise.resolve(true));
28 | const v1 = await schema1.parseAsync("asdf");
29 | expect(v1).toEqual("asdf");
30 |
31 | const schema2 = z.string().refine((_val) => Promise.resolve(false));
32 | await expect(schema2.parseAsync("asdf")).rejects.toBeDefined();
33 |
34 | const schema3 = z.string().refine((_val) => Promise.resolve(true));
35 | await expect(schema3.parseAsync("asdf")).resolves.toEqual("asdf");
36 | return await expect(schema3.parseAsync("qwer")).resolves.toEqual("qwer");
37 | });
38 |
39 | test("parseAsync async with value", async () => {
40 | const schema1 = z.string().refine(async (val) => {
41 | return val.length > 5;
42 | });
43 | await expect(schema1.parseAsync("asdf")).rejects.toBeDefined();
44 |
45 | const v = await schema1.parseAsync("asdf123");
46 | return await expect(v).toEqual("asdf123");
47 | });
48 |
--------------------------------------------------------------------------------
/src/benchmarks/union.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | import { z } from "../index";
4 |
5 | const doubleSuite = new Benchmark.Suite("z.union: double");
6 | const manySuite = new Benchmark.Suite("z.union: many");
7 |
8 | const aSchema = z.object({
9 | type: z.literal("a"),
10 | });
11 | const objA = {
12 | type: "a",
13 | };
14 |
15 | const bSchema = z.object({
16 | type: z.literal("b"),
17 | });
18 | const objB = {
19 | type: "b",
20 | };
21 |
22 | const cSchema = z.object({
23 | type: z.literal("c"),
24 | });
25 | const objC = {
26 | type: "c",
27 | };
28 |
29 | const dSchema = z.object({
30 | type: z.literal("d"),
31 | });
32 |
33 | const double = z.union([aSchema, bSchema]);
34 | const many = z.union([aSchema, bSchema, cSchema, dSchema]);
35 |
36 | doubleSuite
37 | .add("valid: a", () => {
38 | double.parse(objA);
39 | })
40 | .add("valid: b", () => {
41 | double.parse(objB);
42 | })
43 | .add("invalid: null", () => {
44 | try {
45 | double.parse(null);
46 | } catch (err) {}
47 | })
48 | .add("invalid: wrong shape", () => {
49 | try {
50 | double.parse(objC);
51 | } catch (err) {}
52 | })
53 | .on("cycle", (e: Benchmark.Event) => {
54 | console.log(`${(doubleSuite as any).name}: ${e.target}`);
55 | });
56 |
57 | manySuite
58 | .add("valid: a", () => {
59 | many.parse(objA);
60 | })
61 | .add("valid: c", () => {
62 | many.parse(objC);
63 | })
64 | .add("invalid: null", () => {
65 | try {
66 | many.parse(null);
67 | } catch (err) {}
68 | })
69 | .add("invalid: wrong shape", () => {
70 | try {
71 | many.parse({ type: "unknown" });
72 | } catch (err) {}
73 | })
74 | .on("cycle", (e: Benchmark.Event) => {
75 | console.log(`${(manySuite as any).name}: ${e.target}`);
76 | });
77 |
78 | export default {
79 | suites: [doubleSuite, manySuite],
80 | };
81 |
--------------------------------------------------------------------------------
/deno/lib/benchmarks/union.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | import { z } from "../index.ts";
4 |
5 | const doubleSuite = new Benchmark.Suite("z.union: double");
6 | const manySuite = new Benchmark.Suite("z.union: many");
7 |
8 | const aSchema = z.object({
9 | type: z.literal("a"),
10 | });
11 | const objA = {
12 | type: "a",
13 | };
14 |
15 | const bSchema = z.object({
16 | type: z.literal("b"),
17 | });
18 | const objB = {
19 | type: "b",
20 | };
21 |
22 | const cSchema = z.object({
23 | type: z.literal("c"),
24 | });
25 | const objC = {
26 | type: "c",
27 | };
28 |
29 | const dSchema = z.object({
30 | type: z.literal("d"),
31 | });
32 |
33 | const double = z.union([aSchema, bSchema]);
34 | const many = z.union([aSchema, bSchema, cSchema, dSchema]);
35 |
36 | doubleSuite
37 | .add("valid: a", () => {
38 | double.parse(objA);
39 | })
40 | .add("valid: b", () => {
41 | double.parse(objB);
42 | })
43 | .add("invalid: null", () => {
44 | try {
45 | double.parse(null);
46 | } catch (err) {}
47 | })
48 | .add("invalid: wrong shape", () => {
49 | try {
50 | double.parse(objC);
51 | } catch (err) {}
52 | })
53 | .on("cycle", (e: Benchmark.Event) => {
54 | console.log(`${(doubleSuite as any).name}: ${e.target}`);
55 | });
56 |
57 | manySuite
58 | .add("valid: a", () => {
59 | many.parse(objA);
60 | })
61 | .add("valid: c", () => {
62 | many.parse(objC);
63 | })
64 | .add("invalid: null", () => {
65 | try {
66 | many.parse(null);
67 | } catch (err) {}
68 | })
69 | .add("invalid: wrong shape", () => {
70 | try {
71 | many.parse({ type: "unknown" });
72 | } catch (err) {}
73 | })
74 | .on("cycle", (e: Benchmark.Event) => {
75 | console.log(`${(manySuite as any).name}: ${e.target}`);
76 | });
77 |
78 | export default {
79 | suites: [doubleSuite, manySuite],
80 | };
81 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/unions.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import * as z from "../index.ts";
6 |
7 | test("function parsing", () => {
8 | const schema = z.union([
9 | z.string().refine(() => false),
10 | z.number().refine(() => false),
11 | ]);
12 | const result = schema.safeParse("asdf");
13 | expect(result.success).toEqual(false);
14 | });
15 |
16 | test("union 2", () => {
17 | const result = z
18 | .union([z.number(), z.string().refine(() => false)])
19 | .safeParse("a");
20 | expect(result.success).toEqual(false);
21 | });
22 |
23 | test("return valid over invalid", () => {
24 | const schema = z.union([
25 | z.object({
26 | email: z.string().email(),
27 | }),
28 | z.string(),
29 | ]);
30 | expect(schema.parse("asdf")).toEqual("asdf");
31 | expect(schema.parse({ email: "asdlkjf@lkajsdf.com" })).toEqual({
32 | email: "asdlkjf@lkajsdf.com",
33 | });
34 | });
35 |
36 | test("return dirty result over aborted", () => {
37 | const result = z
38 | .union([z.number(), z.string().refine(() => false)])
39 | .safeParse("a");
40 | expect(result.success).toEqual(false);
41 | if (!result.success) {
42 | expect(result.error.issues).toEqual([
43 | {
44 | code: "custom",
45 | message: "Invalid input",
46 | path: [],
47 | },
48 | ]);
49 | }
50 | });
51 |
52 | test("options getter", async () => {
53 | const union = z.union([z.string(), z.number()]);
54 | union.options[0].parse("asdf");
55 | union.options[1].parse(1234);
56 | await union.options[0].parseAsync("asdf");
57 | await union.options[1].parseAsync(1234);
58 | });
59 |
60 | test("readonly union", async () => {
61 | const options = [z.string(), z.number()] as const;
62 | const union = z.union(options);
63 | union.parse("asdf");
64 | union.parse(12);
65 | });
66 |
--------------------------------------------------------------------------------
/src/benchmarks/object.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | import { z } from "../index";
4 |
5 | const emptySuite = new Benchmark.Suite("z.object: empty");
6 | const shortSuite = new Benchmark.Suite("z.object: short");
7 | const longSuite = new Benchmark.Suite("z.object: long");
8 |
9 | const empty = z.object({});
10 | const short = z.object({
11 | string: z.string(),
12 | });
13 | const long = z.object({
14 | string: z.string(),
15 | number: z.number(),
16 | boolean: z.boolean(),
17 | });
18 |
19 | emptySuite
20 | .add("valid", () => {
21 | empty.parse({});
22 | })
23 | .add("valid: extra keys", () => {
24 | empty.parse({ string: "string" });
25 | })
26 | .add("invalid: null", () => {
27 | try {
28 | empty.parse(null);
29 | } catch (err) {}
30 | })
31 | .on("cycle", (e: Benchmark.Event) => {
32 | console.log(`${(emptySuite as any).name}: ${e.target}`);
33 | });
34 |
35 | shortSuite
36 | .add("valid", () => {
37 | short.parse({ string: "string" });
38 | })
39 | .add("valid: extra keys", () => {
40 | short.parse({ string: "string", number: 42 });
41 | })
42 | .add("invalid: null", () => {
43 | try {
44 | short.parse(null);
45 | } catch (err) {}
46 | })
47 | .on("cycle", (e: Benchmark.Event) => {
48 | console.log(`${(shortSuite as any).name}: ${e.target}`);
49 | });
50 |
51 | longSuite
52 | .add("valid", () => {
53 | long.parse({ string: "string", number: 42, boolean: true });
54 | })
55 | .add("valid: extra keys", () => {
56 | long.parse({ string: "string", number: 42, boolean: true, list: [] });
57 | })
58 | .add("invalid: null", () => {
59 | try {
60 | long.parse(null);
61 | } catch (err) {}
62 | })
63 | .on("cycle", (e: Benchmark.Event) => {
64 | console.log(`${(longSuite as any).name}: ${e.target}`);
65 | });
66 |
67 | export default {
68 | suites: [emptySuite, shortSuite, longSuite],
69 | };
70 |
--------------------------------------------------------------------------------
/deno/lib/benchmarks/object.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | import { z } from "../index.ts";
4 |
5 | const emptySuite = new Benchmark.Suite("z.object: empty");
6 | const shortSuite = new Benchmark.Suite("z.object: short");
7 | const longSuite = new Benchmark.Suite("z.object: long");
8 |
9 | const empty = z.object({});
10 | const short = z.object({
11 | string: z.string(),
12 | });
13 | const long = z.object({
14 | string: z.string(),
15 | number: z.number(),
16 | boolean: z.boolean(),
17 | });
18 |
19 | emptySuite
20 | .add("valid", () => {
21 | empty.parse({});
22 | })
23 | .add("valid: extra keys", () => {
24 | empty.parse({ string: "string" });
25 | })
26 | .add("invalid: null", () => {
27 | try {
28 | empty.parse(null);
29 | } catch (err) {}
30 | })
31 | .on("cycle", (e: Benchmark.Event) => {
32 | console.log(`${(emptySuite as any).name}: ${e.target}`);
33 | });
34 |
35 | shortSuite
36 | .add("valid", () => {
37 | short.parse({ string: "string" });
38 | })
39 | .add("valid: extra keys", () => {
40 | short.parse({ string: "string", number: 42 });
41 | })
42 | .add("invalid: null", () => {
43 | try {
44 | short.parse(null);
45 | } catch (err) {}
46 | })
47 | .on("cycle", (e: Benchmark.Event) => {
48 | console.log(`${(shortSuite as any).name}: ${e.target}`);
49 | });
50 |
51 | longSuite
52 | .add("valid", () => {
53 | long.parse({ string: "string", number: 42, boolean: true });
54 | })
55 | .add("valid: extra keys", () => {
56 | long.parse({ string: "string", number: 42, boolean: true, list: [] });
57 | })
58 | .add("invalid: null", () => {
59 | try {
60 | long.parse(null);
61 | } catch (err) {}
62 | })
63 | .on("cycle", (e: Benchmark.Event) => {
64 | console.log(`${(longSuite as any).name}: ${e.target}`);
65 | });
66 |
67 | export default {
68 | suites: [emptySuite, shortSuite, longSuite],
69 | };
70 |
--------------------------------------------------------------------------------
/src/benchmarks/discriminatedUnion.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | import { z } from "../index";
4 |
5 | const doubleSuite = new Benchmark.Suite("z.discriminatedUnion: double");
6 | const manySuite = new Benchmark.Suite("z.discriminatedUnion: many");
7 |
8 | const aSchema = z.object({
9 | type: z.literal("a"),
10 | });
11 | const objA = {
12 | type: "a",
13 | };
14 |
15 | const bSchema = z.object({
16 | type: z.literal("b"),
17 | });
18 | const objB = {
19 | type: "b",
20 | };
21 |
22 | const cSchema = z.object({
23 | type: z.literal("c"),
24 | });
25 | const objC = {
26 | type: "c",
27 | };
28 |
29 | const dSchema = z.object({
30 | type: z.literal("d"),
31 | });
32 |
33 | const double = z.discriminatedUnion("type", [aSchema, bSchema]);
34 | const many = z.discriminatedUnion("type", [aSchema, bSchema, cSchema, dSchema]);
35 |
36 | doubleSuite
37 | .add("valid: a", () => {
38 | double.parse(objA);
39 | })
40 | .add("valid: b", () => {
41 | double.parse(objB);
42 | })
43 | .add("invalid: null", () => {
44 | try {
45 | double.parse(null);
46 | } catch (err) {}
47 | })
48 | .add("invalid: wrong shape", () => {
49 | try {
50 | double.parse(objC);
51 | } catch (err) {}
52 | })
53 | .on("cycle", (e: Benchmark.Event) => {
54 | console.log(`${(doubleSuite as any).name}: ${e.target}`);
55 | });
56 |
57 | manySuite
58 | .add("valid: a", () => {
59 | many.parse(objA);
60 | })
61 | .add("valid: c", () => {
62 | many.parse(objC);
63 | })
64 | .add("invalid: null", () => {
65 | try {
66 | many.parse(null);
67 | } catch (err) {}
68 | })
69 | .add("invalid: wrong shape", () => {
70 | try {
71 | many.parse({ type: "unknown" });
72 | } catch (err) {}
73 | })
74 | .on("cycle", (e: Benchmark.Event) => {
75 | console.log(`${(manySuite as any).name}: ${e.target}`);
76 | });
77 |
78 | export default {
79 | suites: [doubleSuite, manySuite],
80 | };
81 |
--------------------------------------------------------------------------------
/deno/lib/benchmarks/discriminatedUnion.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | import { z } from "../index.ts";
4 |
5 | const doubleSuite = new Benchmark.Suite("z.discriminatedUnion: double");
6 | const manySuite = new Benchmark.Suite("z.discriminatedUnion: many");
7 |
8 | const aSchema = z.object({
9 | type: z.literal("a"),
10 | });
11 | const objA = {
12 | type: "a",
13 | };
14 |
15 | const bSchema = z.object({
16 | type: z.literal("b"),
17 | });
18 | const objB = {
19 | type: "b",
20 | };
21 |
22 | const cSchema = z.object({
23 | type: z.literal("c"),
24 | });
25 | const objC = {
26 | type: "c",
27 | };
28 |
29 | const dSchema = z.object({
30 | type: z.literal("d"),
31 | });
32 |
33 | const double = z.discriminatedUnion("type", [aSchema, bSchema]);
34 | const many = z.discriminatedUnion("type", [aSchema, bSchema, cSchema, dSchema]);
35 |
36 | doubleSuite
37 | .add("valid: a", () => {
38 | double.parse(objA);
39 | })
40 | .add("valid: b", () => {
41 | double.parse(objB);
42 | })
43 | .add("invalid: null", () => {
44 | try {
45 | double.parse(null);
46 | } catch (err) {}
47 | })
48 | .add("invalid: wrong shape", () => {
49 | try {
50 | double.parse(objC);
51 | } catch (err) {}
52 | })
53 | .on("cycle", (e: Benchmark.Event) => {
54 | console.log(`${(doubleSuite as any).name}: ${e.target}`);
55 | });
56 |
57 | manySuite
58 | .add("valid: a", () => {
59 | many.parse(objA);
60 | })
61 | .add("valid: c", () => {
62 | many.parse(objC);
63 | })
64 | .add("invalid: null", () => {
65 | try {
66 | many.parse(null);
67 | } catch (err) {}
68 | })
69 | .add("invalid: wrong shape", () => {
70 | try {
71 | many.parse({ type: "unknown" });
72 | } catch (err) {}
73 | })
74 | .on("cycle", (e: Benchmark.Event) => {
75 | console.log(`${(manySuite as any).name}: ${e.target}`);
76 | });
77 |
78 | export default {
79 | suites: [doubleSuite, manySuite],
80 | };
81 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: test
2 |
3 | on:
4 | push:
5 | branches:
6 | - "main"
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | test-node:
13 | runs-on: ubuntu-latest
14 | strategy:
15 | matrix:
16 | node: ["latest"]
17 | typescript: ["5.0", "latest"]
18 | name: Test with TypeScript ${{ matrix.typescript }} on Node ${{ matrix.node }}
19 | steps:
20 | - uses: actions/checkout@v4
21 | - uses: actions/setup-node@v4
22 | with:
23 | node-version: ${{ matrix.node }}
24 | - run: yarn install
25 | - run: yarn add typescript@${{ matrix.typescript }}
26 | - run: yarn build
27 | - run: yarn test
28 |
29 | test-deno:
30 | runs-on: ubuntu-latest
31 | strategy:
32 | matrix:
33 | deno: ["v1.x"]
34 | name: Test with Deno ${{ matrix.deno }}
35 | steps:
36 | - uses: actions/checkout@v4
37 | - uses: actions/setup-node@v4
38 | with:
39 | node-version: latest
40 | - uses: denoland/setup-deno@v1
41 | with:
42 | deno-version: ${{ matrix.deno }}
43 | - run: yarn install
44 | - run: yarn build:deno
45 | - run: deno --version
46 | - run: deno test
47 | working-directory: ./deno/lib
48 | - run: deno run ./index.ts
49 | working-directory: ./deno/lib
50 | - run: deno run ./mod.ts
51 | working-directory: ./deno/lib
52 | - run: |
53 | deno bundle ./mod.ts ./bundle.js
54 | deno run ./bundle.js
55 | working-directory: ./deno/lib
56 |
57 | lint:
58 | runs-on: ubuntu-latest
59 | strategy:
60 | matrix:
61 | node: ["latest"]
62 | name: Lint on Node ${{ matrix.node }}
63 | steps:
64 | - uses: actions/checkout@v4
65 | - uses: actions/setup-node@v4
66 | with:
67 | node-version: ${{ matrix.node }}
68 | - run: yarn install
69 | - run: yarn prettier:check
70 | - run: yarn lint:check
71 |
--------------------------------------------------------------------------------
/src/__tests__/branded.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import { util } from "../helpers/util";
5 | import * as z from "../index";
6 |
7 | test("branded types", () => {
8 | const mySchema = z
9 | .object({
10 | name: z.string(),
11 | })
12 | .brand<"superschema">();
13 |
14 | // simple branding
15 | type MySchema = z.infer;
16 | util.assertEqual<
17 | MySchema,
18 | { name: string } & { [z.BRAND]: { superschema: true } }
19 | >(true);
20 |
21 | const doStuff = (arg: MySchema) => arg;
22 | doStuff(mySchema.parse({ name: "hello there" }));
23 |
24 | // inheritance
25 | const extendedSchema = mySchema.brand<"subschema">();
26 | type ExtendedSchema = z.infer;
27 | util.assertEqual<
28 | ExtendedSchema,
29 | { name: string } & z.BRAND<"superschema"> & z.BRAND<"subschema">
30 | >(true);
31 |
32 | doStuff(extendedSchema.parse({ name: "hello again" }));
33 |
34 | // number branding
35 | const numberSchema = z.number().brand<42>();
36 | type NumberSchema = z.infer;
37 | util.assertEqual(true);
38 |
39 | // symbol branding
40 | const MyBrand: unique symbol = Symbol("hello");
41 | type MyBrand = typeof MyBrand;
42 | const symbolBrand = z.number().brand<"sup">().brand();
43 | type SymbolBrand = z.infer;
44 | // number & { [z.BRAND]: { sup: true, [MyBrand]: true } }
45 | util.assertEqual & z.BRAND>(
46 | true
47 | );
48 |
49 | // keeping brands out of input types
50 | const age = z.number().brand<"age">();
51 |
52 | type Age = z.infer;
53 | type AgeInput = z.input;
54 |
55 | util.assertEqual(false);
56 | util.assertEqual(true);
57 | util.assertEqual, Age>(true);
58 |
59 | // @ts-expect-error
60 | doStuff({ name: "hello there!" });
61 | });
62 |
--------------------------------------------------------------------------------
/src/benchmarks/ipv4.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | const suite = new Benchmark.Suite("ipv4");
4 |
5 | const DATA = "127.0.0.1";
6 | const ipv4RegexA =
7 | /^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/;
8 | const ipv4RegexB =
9 | /^(?:(?:(?=(25[0-5]))\1|(?=(2[0-4][0-9]))\2|(?=(1[0-9]{2}))\3|(?=([0-9]{1,2}))\4)\.){3}(?:(?=(25[0-5]))\5|(?=(2[0-4][0-9]))\6|(?=(1[0-9]{2}))\7|(?=([0-9]{1,2}))\8)$/;
10 | const ipv4RegexC =
11 | /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
12 | const ipv4RegexD =
13 | /^(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/;
14 | const ipv4RegexE =
15 | /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.){3}(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)$/;
16 | const ipv4RegexF = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/;
17 | const ipv4RegexG = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$/;
18 | const ipv4RegexH = /^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$/;
19 | const ipv4RegexI =
20 | /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
21 |
22 | suite
23 | .add("A", () => {
24 | return ipv4RegexA.test(DATA);
25 | })
26 | .add("B", () => {
27 | return ipv4RegexB.test(DATA);
28 | })
29 | .add("C", () => {
30 | return ipv4RegexC.test(DATA);
31 | })
32 | .add("D", () => {
33 | return ipv4RegexD.test(DATA);
34 | })
35 | .add("E", () => {
36 | return ipv4RegexE.test(DATA);
37 | })
38 | .add("F", () => {
39 | return ipv4RegexF.test(DATA);
40 | })
41 | .add("G", () => {
42 | return ipv4RegexG.test(DATA);
43 | })
44 | .add("H", () => {
45 | return ipv4RegexH.test(DATA);
46 | })
47 | .add("I", () => {
48 | return ipv4RegexI.test(DATA);
49 | })
50 | .on("cycle", (e: Benchmark.Event) => {
51 | console.log(`${suite.name!}: ${e.target}`);
52 | });
53 |
54 | export default {
55 | suites: [suite],
56 | };
57 |
58 | if (require.main === module) {
59 | suite.run();
60 | }
61 |
--------------------------------------------------------------------------------
/deno/lib/benchmarks/ipv4.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | const suite = new Benchmark.Suite("ipv4");
4 |
5 | const DATA = "127.0.0.1";
6 | const ipv4RegexA =
7 | /^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/;
8 | const ipv4RegexB =
9 | /^(?:(?:(?=(25[0-5]))\1|(?=(2[0-4][0-9]))\2|(?=(1[0-9]{2}))\3|(?=([0-9]{1,2}))\4)\.){3}(?:(?=(25[0-5]))\5|(?=(2[0-4][0-9]))\6|(?=(1[0-9]{2}))\7|(?=([0-9]{1,2}))\8)$/;
10 | const ipv4RegexC =
11 | /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
12 | const ipv4RegexD =
13 | /^(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/;
14 | const ipv4RegexE =
15 | /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.){3}(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)$/;
16 | const ipv4RegexF = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/;
17 | const ipv4RegexG = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$/;
18 | const ipv4RegexH = /^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$/;
19 | const ipv4RegexI =
20 | /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
21 |
22 | suite
23 | .add("A", () => {
24 | return ipv4RegexA.test(DATA);
25 | })
26 | .add("B", () => {
27 | return ipv4RegexB.test(DATA);
28 | })
29 | .add("C", () => {
30 | return ipv4RegexC.test(DATA);
31 | })
32 | .add("D", () => {
33 | return ipv4RegexD.test(DATA);
34 | })
35 | .add("E", () => {
36 | return ipv4RegexE.test(DATA);
37 | })
38 | .add("F", () => {
39 | return ipv4RegexF.test(DATA);
40 | })
41 | .add("G", () => {
42 | return ipv4RegexG.test(DATA);
43 | })
44 | .add("H", () => {
45 | return ipv4RegexH.test(DATA);
46 | })
47 | .add("I", () => {
48 | return ipv4RegexI.test(DATA);
49 | })
50 | .on("cycle", (e: Benchmark.Event) => {
51 | console.log(`${suite.name!}: ${e.target}`);
52 | });
53 |
54 | export default {
55 | suites: [suite],
56 | };
57 |
58 | if (require.main === module) {
59 | suite.run();
60 | }
61 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/branded.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import { util } from "../helpers/util.ts";
6 | import * as z from "../index.ts";
7 |
8 | test("branded types", () => {
9 | const mySchema = z
10 | .object({
11 | name: z.string(),
12 | })
13 | .brand<"superschema">();
14 |
15 | // simple branding
16 | type MySchema = z.infer;
17 | util.assertEqual<
18 | MySchema,
19 | { name: string } & { [z.BRAND]: { superschema: true } }
20 | >(true);
21 |
22 | const doStuff = (arg: MySchema) => arg;
23 | doStuff(mySchema.parse({ name: "hello there" }));
24 |
25 | // inheritance
26 | const extendedSchema = mySchema.brand<"subschema">();
27 | type ExtendedSchema = z.infer;
28 | util.assertEqual<
29 | ExtendedSchema,
30 | { name: string } & z.BRAND<"superschema"> & z.BRAND<"subschema">
31 | >(true);
32 |
33 | doStuff(extendedSchema.parse({ name: "hello again" }));
34 |
35 | // number branding
36 | const numberSchema = z.number().brand<42>();
37 | type NumberSchema = z.infer;
38 | util.assertEqual(true);
39 |
40 | // symbol branding
41 | const MyBrand: unique symbol = Symbol("hello");
42 | type MyBrand = typeof MyBrand;
43 | const symbolBrand = z.number().brand<"sup">().brand();
44 | type SymbolBrand = z.infer;
45 | // number & { [z.BRAND]: { sup: true, [MyBrand]: true } }
46 | util.assertEqual & z.BRAND>(
47 | true
48 | );
49 |
50 | // keeping brands out of input types
51 | const age = z.number().brand<"age">();
52 |
53 | type Age = z.infer;
54 | type AgeInput = z.input;
55 |
56 | util.assertEqual(false);
57 | util.assertEqual(true);
58 | util.assertEqual, Age>(true);
59 |
60 | // @ts-expect-error
61 | doStuff({ name: "hello there!" });
62 | });
63 |
--------------------------------------------------------------------------------
/src/benchmarks/datetime.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | const datetimeValidationSuite = new Benchmark.Suite("datetime");
4 |
5 | const DATA = "2021-01-01";
6 | const MONTHS_31 = new Set([1, 3, 5, 7, 8, 10, 12]);
7 | const MONTHS_30 = new Set([4, 6, 9, 11]);
8 |
9 | const simpleDatetimeRegex = /^(\d{4})-(\d{2})-(\d{2})$/;
10 | const datetimeRegexNoLeapYearValidation =
11 | /^\d{4}-((0[13578]|10|12)-31|(0[13-9]|1[0-2])-30|(0[1-9]|1[0-2])-(0[1-9]|1\d|2\d))$/;
12 | const datetimeRegexWithLeapYearValidation =
13 | /^((\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\d|3[01])|(0[469]|11)-(0[1-9]|[12]\d|30)|(02)-(0[1-9]|1\d|2[0-8])))$/;
14 |
15 | datetimeValidationSuite
16 | .add("new Date()", () => {
17 | return !isNaN(new Date(DATA).getTime());
18 | })
19 | .add("regex (no validation)", () => {
20 | return simpleDatetimeRegex.test(DATA);
21 | })
22 | .add("regex (no leap year)", () => {
23 | return datetimeRegexNoLeapYearValidation.test(DATA);
24 | })
25 | .add("regex (w/ leap year)", () => {
26 | return datetimeRegexWithLeapYearValidation.test(DATA);
27 | })
28 | .add("capture groups + code", () => {
29 | const match = DATA.match(simpleDatetimeRegex);
30 | if (!match) return false;
31 |
32 | // Extract year, month, and day from the capture groups
33 | const year = Number.parseInt(match[1], 10);
34 | const month = Number.parseInt(match[2], 10); // month is 0-indexed in JavaScript Date, so subtract 1
35 | const day = Number.parseInt(match[3], 10);
36 |
37 | if (month === 2) {
38 | if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
39 | return day <= 29;
40 | }
41 | return day <= 28;
42 | }
43 | if (MONTHS_30.has(month)) {
44 | return day <= 30;
45 | }
46 | if (MONTHS_31.has(month)) {
47 | return day <= 31;
48 | }
49 | return false;
50 | })
51 |
52 | .on("cycle", (e: Benchmark.Event) => {
53 | console.log(`${datetimeValidationSuite.name!}: ${e.target}`);
54 | });
55 |
56 | export default {
57 | suites: [datetimeValidationSuite],
58 | };
59 |
--------------------------------------------------------------------------------
/deno/lib/benchmarks/datetime.ts:
--------------------------------------------------------------------------------
1 | import Benchmark from "benchmark";
2 |
3 | const datetimeValidationSuite = new Benchmark.Suite("datetime");
4 |
5 | const DATA = "2021-01-01";
6 | const MONTHS_31 = new Set([1, 3, 5, 7, 8, 10, 12]);
7 | const MONTHS_30 = new Set([4, 6, 9, 11]);
8 |
9 | const simpleDatetimeRegex = /^(\d{4})-(\d{2})-(\d{2})$/;
10 | const datetimeRegexNoLeapYearValidation =
11 | /^\d{4}-((0[13578]|10|12)-31|(0[13-9]|1[0-2])-30|(0[1-9]|1[0-2])-(0[1-9]|1\d|2\d))$/;
12 | const datetimeRegexWithLeapYearValidation =
13 | /^((\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\d|3[01])|(0[469]|11)-(0[1-9]|[12]\d|30)|(02)-(0[1-9]|1\d|2[0-8])))$/;
14 |
15 | datetimeValidationSuite
16 | .add("new Date()", () => {
17 | return !isNaN(new Date(DATA).getTime());
18 | })
19 | .add("regex (no validation)", () => {
20 | return simpleDatetimeRegex.test(DATA);
21 | })
22 | .add("regex (no leap year)", () => {
23 | return datetimeRegexNoLeapYearValidation.test(DATA);
24 | })
25 | .add("regex (w/ leap year)", () => {
26 | return datetimeRegexWithLeapYearValidation.test(DATA);
27 | })
28 | .add("capture groups + code", () => {
29 | const match = DATA.match(simpleDatetimeRegex);
30 | if (!match) return false;
31 |
32 | // Extract year, month, and day from the capture groups
33 | const year = Number.parseInt(match[1], 10);
34 | const month = Number.parseInt(match[2], 10); // month is 0-indexed in JavaScript Date, so subtract 1
35 | const day = Number.parseInt(match[3], 10);
36 |
37 | if (month === 2) {
38 | if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
39 | return day <= 29;
40 | }
41 | return day <= 28;
42 | }
43 | if (MONTHS_30.has(month)) {
44 | return day <= 30;
45 | }
46 | if (MONTHS_31.has(month)) {
47 | return day <= 31;
48 | }
49 | return false;
50 | })
51 |
52 | .on("cycle", (e: Benchmark.Event) => {
53 | console.log(`${datetimeValidationSuite.name!}: ${e.target}`);
54 | });
55 |
56 | export default {
57 | suites: [datetimeValidationSuite],
58 | };
59 |
--------------------------------------------------------------------------------
/src/__tests__/bigint.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import * as z from "../index";
5 |
6 | const gtFive = z.bigint().gt(BigInt(5));
7 | const gteFive = z.bigint().gte(BigInt(5));
8 | const ltFive = z.bigint().lt(BigInt(5));
9 | const lteFive = z.bigint().lte(BigInt(5));
10 | const positive = z.bigint().positive();
11 | const negative = z.bigint().negative();
12 | const nonnegative = z.bigint().nonnegative();
13 | const nonpositive = z.bigint().nonpositive();
14 | const multipleOfFive = z.bigint().multipleOf(BigInt(5));
15 |
16 | test("passing validations", () => {
17 | z.bigint().parse(BigInt(1));
18 | z.bigint().parse(BigInt(0));
19 | z.bigint().parse(BigInt(-1));
20 | gtFive.parse(BigInt(6));
21 | gteFive.parse(BigInt(5));
22 | gteFive.parse(BigInt(6));
23 | ltFive.parse(BigInt(4));
24 | lteFive.parse(BigInt(5));
25 | lteFive.parse(BigInt(4));
26 | positive.parse(BigInt(3));
27 | negative.parse(BigInt(-2));
28 | nonnegative.parse(BigInt(0));
29 | nonnegative.parse(BigInt(7));
30 | nonpositive.parse(BigInt(0));
31 | nonpositive.parse(BigInt(-12));
32 | multipleOfFive.parse(BigInt(15));
33 | });
34 |
35 | test("failing validations", () => {
36 | expect(() => gtFive.parse(BigInt(5))).toThrow();
37 | expect(() => gteFive.parse(BigInt(4))).toThrow();
38 | expect(() => ltFive.parse(BigInt(5))).toThrow();
39 | expect(() => lteFive.parse(BigInt(6))).toThrow();
40 | expect(() => positive.parse(BigInt(0))).toThrow();
41 | expect(() => positive.parse(BigInt(-2))).toThrow();
42 | expect(() => negative.parse(BigInt(0))).toThrow();
43 | expect(() => negative.parse(BigInt(3))).toThrow();
44 | expect(() => nonnegative.parse(BigInt(-1))).toThrow();
45 | expect(() => nonpositive.parse(BigInt(1))).toThrow();
46 | expect(() => multipleOfFive.parse(BigInt(13))).toThrow();
47 | });
48 |
49 | test("min max getters", () => {
50 | expect(z.bigint().min(BigInt(5)).minValue).toEqual(BigInt(5));
51 | expect(z.bigint().min(BigInt(5)).min(BigInt(10)).minValue).toEqual(
52 | BigInt(10)
53 | );
54 |
55 | expect(z.bigint().max(BigInt(5)).maxValue).toEqual(BigInt(5));
56 | expect(z.bigint().max(BigInt(5)).max(BigInt(1)).maxValue).toEqual(BigInt(1));
57 | });
58 |
--------------------------------------------------------------------------------
/src/__tests__/array.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import { util } from "../helpers/util";
5 | import * as z from "../index";
6 |
7 | const minTwo = z.string().array().min(2);
8 | const maxTwo = z.string().array().max(2);
9 | const justTwo = z.string().array().length(2);
10 | const intNum = z.string().array().nonempty();
11 | const nonEmptyMax = z.string().array().nonempty().max(2);
12 |
13 | type t1 = z.infer;
14 | util.assertEqual<[string, ...string[]], t1>(true);
15 |
16 | type t2 = z.infer;
17 | util.assertEqual(true);
18 |
19 | test("passing validations", () => {
20 | minTwo.parse(["a", "a"]);
21 | minTwo.parse(["a", "a", "a"]);
22 | maxTwo.parse(["a", "a"]);
23 | maxTwo.parse(["a"]);
24 | justTwo.parse(["a", "a"]);
25 | intNum.parse(["a"]);
26 | nonEmptyMax.parse(["a"]);
27 | });
28 |
29 | test("failing validations", () => {
30 | expect(() => minTwo.parse(["a"])).toThrow();
31 | expect(() => maxTwo.parse(["a", "a", "a"])).toThrow();
32 | expect(() => justTwo.parse(["a"])).toThrow();
33 | expect(() => justTwo.parse(["a", "a", "a"])).toThrow();
34 | expect(() => intNum.parse([])).toThrow();
35 | expect(() => nonEmptyMax.parse([])).toThrow();
36 | expect(() => nonEmptyMax.parse(["a", "a", "a"])).toThrow();
37 | });
38 |
39 | test("parse empty array in nonempty", () => {
40 | expect(() =>
41 | z
42 | .array(z.string())
43 | .nonempty()
44 | .parse([] as any)
45 | ).toThrow();
46 | });
47 |
48 | test("get element", () => {
49 | justTwo.element.parse("asdf");
50 | expect(() => justTwo.element.parse(12)).toThrow();
51 | });
52 |
53 | test("continue parsing despite array size error", () => {
54 | const schema = z.object({
55 | people: z.string().array().min(2),
56 | });
57 |
58 | const result = schema.safeParse({
59 | people: [123],
60 | });
61 | expect(result.success).toEqual(false);
62 | if (!result.success) {
63 | expect(result.error.issues.length).toEqual(2);
64 | }
65 | });
66 |
67 | test("parse should fail given sparse array", () => {
68 | const schema = z.array(z.string()).nonempty().min(1).max(3);
69 |
70 | expect(() => schema.parse(new Array(3))).toThrow();
71 | });
72 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/bigint.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import * as z from "../index.ts";
6 |
7 | const gtFive = z.bigint().gt(BigInt(5));
8 | const gteFive = z.bigint().gte(BigInt(5));
9 | const ltFive = z.bigint().lt(BigInt(5));
10 | const lteFive = z.bigint().lte(BigInt(5));
11 | const positive = z.bigint().positive();
12 | const negative = z.bigint().negative();
13 | const nonnegative = z.bigint().nonnegative();
14 | const nonpositive = z.bigint().nonpositive();
15 | const multipleOfFive = z.bigint().multipleOf(BigInt(5));
16 |
17 | test("passing validations", () => {
18 | z.bigint().parse(BigInt(1));
19 | z.bigint().parse(BigInt(0));
20 | z.bigint().parse(BigInt(-1));
21 | gtFive.parse(BigInt(6));
22 | gteFive.parse(BigInt(5));
23 | gteFive.parse(BigInt(6));
24 | ltFive.parse(BigInt(4));
25 | lteFive.parse(BigInt(5));
26 | lteFive.parse(BigInt(4));
27 | positive.parse(BigInt(3));
28 | negative.parse(BigInt(-2));
29 | nonnegative.parse(BigInt(0));
30 | nonnegative.parse(BigInt(7));
31 | nonpositive.parse(BigInt(0));
32 | nonpositive.parse(BigInt(-12));
33 | multipleOfFive.parse(BigInt(15));
34 | });
35 |
36 | test("failing validations", () => {
37 | expect(() => gtFive.parse(BigInt(5))).toThrow();
38 | expect(() => gteFive.parse(BigInt(4))).toThrow();
39 | expect(() => ltFive.parse(BigInt(5))).toThrow();
40 | expect(() => lteFive.parse(BigInt(6))).toThrow();
41 | expect(() => positive.parse(BigInt(0))).toThrow();
42 | expect(() => positive.parse(BigInt(-2))).toThrow();
43 | expect(() => negative.parse(BigInt(0))).toThrow();
44 | expect(() => negative.parse(BigInt(3))).toThrow();
45 | expect(() => nonnegative.parse(BigInt(-1))).toThrow();
46 | expect(() => nonpositive.parse(BigInt(1))).toThrow();
47 | expect(() => multipleOfFive.parse(BigInt(13))).toThrow();
48 | });
49 |
50 | test("min max getters", () => {
51 | expect(z.bigint().min(BigInt(5)).minValue).toEqual(BigInt(5));
52 | expect(z.bigint().min(BigInt(5)).min(BigInt(10)).minValue).toEqual(
53 | BigInt(10)
54 | );
55 |
56 | expect(z.bigint().max(BigInt(5)).maxValue).toEqual(BigInt(5));
57 | expect(z.bigint().max(BigInt(5)).max(BigInt(1)).maxValue).toEqual(BigInt(1));
58 | });
59 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/array.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import { util } from "../helpers/util.ts";
6 | import * as z from "../index.ts";
7 |
8 | const minTwo = z.string().array().min(2);
9 | const maxTwo = z.string().array().max(2);
10 | const justTwo = z.string().array().length(2);
11 | const intNum = z.string().array().nonempty();
12 | const nonEmptyMax = z.string().array().nonempty().max(2);
13 |
14 | type t1 = z.infer;
15 | util.assertEqual<[string, ...string[]], t1>(true);
16 |
17 | type t2 = z.infer;
18 | util.assertEqual(true);
19 |
20 | test("passing validations", () => {
21 | minTwo.parse(["a", "a"]);
22 | minTwo.parse(["a", "a", "a"]);
23 | maxTwo.parse(["a", "a"]);
24 | maxTwo.parse(["a"]);
25 | justTwo.parse(["a", "a"]);
26 | intNum.parse(["a"]);
27 | nonEmptyMax.parse(["a"]);
28 | });
29 |
30 | test("failing validations", () => {
31 | expect(() => minTwo.parse(["a"])).toThrow();
32 | expect(() => maxTwo.parse(["a", "a", "a"])).toThrow();
33 | expect(() => justTwo.parse(["a"])).toThrow();
34 | expect(() => justTwo.parse(["a", "a", "a"])).toThrow();
35 | expect(() => intNum.parse([])).toThrow();
36 | expect(() => nonEmptyMax.parse([])).toThrow();
37 | expect(() => nonEmptyMax.parse(["a", "a", "a"])).toThrow();
38 | });
39 |
40 | test("parse empty array in nonempty", () => {
41 | expect(() =>
42 | z
43 | .array(z.string())
44 | .nonempty()
45 | .parse([] as any)
46 | ).toThrow();
47 | });
48 |
49 | test("get element", () => {
50 | justTwo.element.parse("asdf");
51 | expect(() => justTwo.element.parse(12)).toThrow();
52 | });
53 |
54 | test("continue parsing despite array size error", () => {
55 | const schema = z.object({
56 | people: z.string().array().min(2),
57 | });
58 |
59 | const result = schema.safeParse({
60 | people: [123],
61 | });
62 | expect(result.success).toEqual(false);
63 | if (!result.success) {
64 | expect(result.error.issues.length).toEqual(2);
65 | }
66 | });
67 |
68 | test("parse should fail given sparse array", () => {
69 | const schema = z.array(z.string()).nonempty().min(1).max(3);
70 |
71 | expect(() => schema.parse(new Array(3))).toThrow();
72 | });
73 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: { browser: true, node: true },
3 | root: true,
4 | parser: "@typescript-eslint/parser",
5 | plugins: [
6 | "@typescript-eslint",
7 | "import",
8 | "simple-import-sort",
9 | "unused-imports",
10 | "ban",
11 | ],
12 | extends: [
13 | "eslint:recommended",
14 | "plugin:@typescript-eslint/recommended",
15 | "prettier",
16 | ],
17 | rules: {
18 | "import/order": 0, // turn off in favor of eslint-plugin-simple-import-sort
19 | "import/no-unresolved": 0,
20 | "import/no-duplicates": 1,
21 |
22 | /**
23 | * eslint-plugin-simple-import-sort @see https://github.com/lydell/eslint-plugin-simple-import-sort
24 | */
25 | "sort-imports": 0, // we use eslint-plugin-import instead
26 | "simple-import-sort/imports": 1,
27 | "simple-import-sort/exports": 1,
28 |
29 | /**
30 | * @typescript-eslint/eslint-plugin @see https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin
31 | */
32 | "@typescript-eslint/no-namespace": "off",
33 | "@typescript-eslint/explicit-module-boundary-types": "off",
34 | "@typescript-eslint/no-explicit-any": "off",
35 | "@typescript-eslint/ban-types": "off",
36 | "@typescript-eslint/no-unused-vars": "off",
37 | "@typescript-eslint/no-empty-function": "off",
38 | "@typescript-eslint/ban-ts-comment": "off",
39 | "@typescript-eslint/no-non-null-assertion": "off",
40 | "@typescript-eslint/no-empty-interface": "off",
41 | /**
42 | * ESLint core rules @see https://eslint.org/docs/rules/
43 | */
44 | "no-case-declarations": "off",
45 | "no-empty": "off",
46 | "no-useless-escape": "off",
47 | "no-control-regex": "off",
48 |
49 | "ban/ban": [
50 | 2,
51 | {
52 | name: ["Object", "keys"],
53 | message:
54 | "Object.keys() is not supported in legacy browsers, use objectKeys()",
55 | },
56 | {
57 | name: ["Object", "setPrototypeOf"],
58 | message: "Object.setPrototypeOf() is not supported in legacy browsers",
59 | },
60 | {
61 | name: ["Number", "isNaN"],
62 | message: "Number.isNaN() is not supported in legacy browsers",
63 | },
64 | {
65 | name: ["Number", "isInteger"],
66 | message: "Number.isInteger() is not supported in legacy browsers",
67 | },
68 | ],
69 | },
70 | };
71 |
--------------------------------------------------------------------------------
/src/__tests__/nativeEnum.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 |
4 | import { util } from "../helpers/util";
5 | import * as z from "../index";
6 |
7 | test("nativeEnum test with consts", () => {
8 | const Fruits: { Apple: "apple"; Banana: "banana" } = {
9 | Apple: "apple",
10 | Banana: "banana",
11 | };
12 | const fruitEnum = z.nativeEnum(Fruits);
13 | type fruitEnum = z.infer;
14 | fruitEnum.parse("apple");
15 | fruitEnum.parse("banana");
16 | fruitEnum.parse(Fruits.Apple);
17 | fruitEnum.parse(Fruits.Banana);
18 | util.assertEqual(true);
19 | });
20 |
21 | test("nativeEnum test with real enum", () => {
22 | enum Fruits {
23 | Apple = "apple",
24 | Banana = "banana",
25 | }
26 | // @ts-ignore
27 | const fruitEnum = z.nativeEnum(Fruits);
28 | type fruitEnum = z.infer;
29 | fruitEnum.parse("apple");
30 | fruitEnum.parse("banana");
31 | fruitEnum.parse(Fruits.Apple);
32 | fruitEnum.parse(Fruits.Banana);
33 | util.assertIs(true);
34 | });
35 |
36 | test("nativeEnum test with const with numeric keys", () => {
37 | const FruitValues = {
38 | Apple: 10,
39 | Banana: 20,
40 | // @ts-ignore
41 | } as const;
42 | const fruitEnum = z.nativeEnum(FruitValues);
43 | type fruitEnum = z.infer;
44 | fruitEnum.parse(10);
45 | fruitEnum.parse(20);
46 | fruitEnum.parse(FruitValues.Apple);
47 | fruitEnum.parse(FruitValues.Banana);
48 | util.assertEqual(true);
49 | });
50 |
51 | test("from enum", () => {
52 | enum Fruits {
53 | Cantaloupe,
54 | Apple = "apple",
55 | Banana = "banana",
56 | }
57 |
58 | const FruitEnum = z.nativeEnum(Fruits as any);
59 | type FruitEnum = z.infer;
60 | FruitEnum.parse(Fruits.Cantaloupe);
61 | FruitEnum.parse(Fruits.Apple);
62 | FruitEnum.parse("apple");
63 | FruitEnum.parse(0);
64 | expect(() => FruitEnum.parse(1)).toThrow();
65 | expect(() => FruitEnum.parse("Apple")).toThrow();
66 | expect(() => FruitEnum.parse("Cantaloupe")).toThrow();
67 | });
68 |
69 | test("from const", () => {
70 | const Greek = {
71 | Alpha: "a",
72 | Beta: "b",
73 | Gamma: 3,
74 | // @ts-ignore
75 | } as const;
76 |
77 | const GreekEnum = z.nativeEnum(Greek);
78 | type GreekEnum = z.infer;
79 | GreekEnum.parse("a");
80 | GreekEnum.parse("b");
81 | GreekEnum.parse(3);
82 | expect(() => GreekEnum.parse("v")).toThrow();
83 | expect(() => GreekEnum.parse("Alpha")).toThrow();
84 | expect(() => GreekEnum.parse(2)).toThrow();
85 |
86 | expect(GreekEnum.enum.Alpha).toEqual("a");
87 | });
88 |
--------------------------------------------------------------------------------
/src/__tests__/firstparty.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { test } from "@jest/globals";
3 |
4 | import { util } from "../helpers/util";
5 | import * as z from "../index";
6 |
7 | test("first party switch", () => {
8 | const myType = z.string() as z.ZodFirstPartySchemaTypes;
9 | const def = myType._def;
10 |
11 | switch (def.typeName) {
12 | case z.ZodFirstPartyTypeKind.ZodString:
13 | break;
14 | case z.ZodFirstPartyTypeKind.ZodNumber:
15 | break;
16 | case z.ZodFirstPartyTypeKind.ZodNaN:
17 | break;
18 | case z.ZodFirstPartyTypeKind.ZodBigInt:
19 | break;
20 | case z.ZodFirstPartyTypeKind.ZodBoolean:
21 | break;
22 | case z.ZodFirstPartyTypeKind.ZodDate:
23 | break;
24 | case z.ZodFirstPartyTypeKind.ZodUndefined:
25 | break;
26 | case z.ZodFirstPartyTypeKind.ZodNull:
27 | break;
28 | case z.ZodFirstPartyTypeKind.ZodAny:
29 | break;
30 | case z.ZodFirstPartyTypeKind.ZodUnknown:
31 | break;
32 | case z.ZodFirstPartyTypeKind.ZodNever:
33 | break;
34 | case z.ZodFirstPartyTypeKind.ZodVoid:
35 | break;
36 | case z.ZodFirstPartyTypeKind.ZodArray:
37 | break;
38 | case z.ZodFirstPartyTypeKind.ZodObject:
39 | break;
40 | case z.ZodFirstPartyTypeKind.ZodUnion:
41 | break;
42 | case z.ZodFirstPartyTypeKind.ZodDiscriminatedUnion:
43 | break;
44 | case z.ZodFirstPartyTypeKind.ZodIntersection:
45 | break;
46 | case z.ZodFirstPartyTypeKind.ZodTuple:
47 | break;
48 | case z.ZodFirstPartyTypeKind.ZodRecord:
49 | break;
50 | case z.ZodFirstPartyTypeKind.ZodMap:
51 | break;
52 | case z.ZodFirstPartyTypeKind.ZodSet:
53 | break;
54 | case z.ZodFirstPartyTypeKind.ZodFunction:
55 | break;
56 | case z.ZodFirstPartyTypeKind.ZodLazy:
57 | break;
58 | case z.ZodFirstPartyTypeKind.ZodLiteral:
59 | break;
60 | case z.ZodFirstPartyTypeKind.ZodEnum:
61 | break;
62 | case z.ZodFirstPartyTypeKind.ZodEffects:
63 | break;
64 | case z.ZodFirstPartyTypeKind.ZodNativeEnum:
65 | break;
66 | case z.ZodFirstPartyTypeKind.ZodOptional:
67 | break;
68 | case z.ZodFirstPartyTypeKind.ZodNullable:
69 | break;
70 | case z.ZodFirstPartyTypeKind.ZodDefault:
71 | break;
72 | case z.ZodFirstPartyTypeKind.ZodCatch:
73 | break;
74 | case z.ZodFirstPartyTypeKind.ZodPromise:
75 | break;
76 | case z.ZodFirstPartyTypeKind.ZodBranded:
77 | break;
78 | case z.ZodFirstPartyTypeKind.ZodPipeline:
79 | break;
80 | case z.ZodFirstPartyTypeKind.ZodSymbol:
81 | break;
82 | case z.ZodFirstPartyTypeKind.ZodReadonly:
83 | break;
84 | default:
85 | util.assertNever(def);
86 | }
87 | });
88 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/nativeEnum.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import { util } from "../helpers/util.ts";
6 | import * as z from "../index.ts";
7 |
8 | test("nativeEnum test with consts", () => {
9 | const Fruits: { Apple: "apple"; Banana: "banana" } = {
10 | Apple: "apple",
11 | Banana: "banana",
12 | };
13 | const fruitEnum = z.nativeEnum(Fruits);
14 | type fruitEnum = z.infer;
15 | fruitEnum.parse("apple");
16 | fruitEnum.parse("banana");
17 | fruitEnum.parse(Fruits.Apple);
18 | fruitEnum.parse(Fruits.Banana);
19 | util.assertEqual(true);
20 | });
21 |
22 | test("nativeEnum test with real enum", () => {
23 | enum Fruits {
24 | Apple = "apple",
25 | Banana = "banana",
26 | }
27 | // @ts-ignore
28 | const fruitEnum = z.nativeEnum(Fruits);
29 | type fruitEnum = z.infer;
30 | fruitEnum.parse("apple");
31 | fruitEnum.parse("banana");
32 | fruitEnum.parse(Fruits.Apple);
33 | fruitEnum.parse(Fruits.Banana);
34 | util.assertIs(true);
35 | });
36 |
37 | test("nativeEnum test with const with numeric keys", () => {
38 | const FruitValues = {
39 | Apple: 10,
40 | Banana: 20,
41 | // @ts-ignore
42 | } as const;
43 | const fruitEnum = z.nativeEnum(FruitValues);
44 | type fruitEnum = z.infer;
45 | fruitEnum.parse(10);
46 | fruitEnum.parse(20);
47 | fruitEnum.parse(FruitValues.Apple);
48 | fruitEnum.parse(FruitValues.Banana);
49 | util.assertEqual(true);
50 | });
51 |
52 | test("from enum", () => {
53 | enum Fruits {
54 | Cantaloupe,
55 | Apple = "apple",
56 | Banana = "banana",
57 | }
58 |
59 | const FruitEnum = z.nativeEnum(Fruits as any);
60 | type FruitEnum = z.infer;
61 | FruitEnum.parse(Fruits.Cantaloupe);
62 | FruitEnum.parse(Fruits.Apple);
63 | FruitEnum.parse("apple");
64 | FruitEnum.parse(0);
65 | expect(() => FruitEnum.parse(1)).toThrow();
66 | expect(() => FruitEnum.parse("Apple")).toThrow();
67 | expect(() => FruitEnum.parse("Cantaloupe")).toThrow();
68 | });
69 |
70 | test("from const", () => {
71 | const Greek = {
72 | Alpha: "a",
73 | Beta: "b",
74 | Gamma: 3,
75 | // @ts-ignore
76 | } as const;
77 |
78 | const GreekEnum = z.nativeEnum(Greek);
79 | type GreekEnum = z.infer;
80 | GreekEnum.parse("a");
81 | GreekEnum.parse("b");
82 | GreekEnum.parse(3);
83 | expect(() => GreekEnum.parse("v")).toThrow();
84 | expect(() => GreekEnum.parse("Alpha")).toThrow();
85 | expect(() => GreekEnum.parse(2)).toThrow();
86 |
87 | expect(GreekEnum.enum.Alpha).toEqual("a");
88 | });
89 |
--------------------------------------------------------------------------------
/deno/lib/__tests__/firstparty.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3 | const test = Deno.test;
4 |
5 | import { util } from "../helpers/util.ts";
6 | import * as z from "../index.ts";
7 |
8 | test("first party switch", () => {
9 | const myType = z.string() as z.ZodFirstPartySchemaTypes;
10 | const def = myType._def;
11 |
12 | switch (def.typeName) {
13 | case z.ZodFirstPartyTypeKind.ZodString:
14 | break;
15 | case z.ZodFirstPartyTypeKind.ZodNumber:
16 | break;
17 | case z.ZodFirstPartyTypeKind.ZodNaN:
18 | break;
19 | case z.ZodFirstPartyTypeKind.ZodBigInt:
20 | break;
21 | case z.ZodFirstPartyTypeKind.ZodBoolean:
22 | break;
23 | case z.ZodFirstPartyTypeKind.ZodDate:
24 | break;
25 | case z.ZodFirstPartyTypeKind.ZodUndefined:
26 | break;
27 | case z.ZodFirstPartyTypeKind.ZodNull:
28 | break;
29 | case z.ZodFirstPartyTypeKind.ZodAny:
30 | break;
31 | case z.ZodFirstPartyTypeKind.ZodUnknown:
32 | break;
33 | case z.ZodFirstPartyTypeKind.ZodNever:
34 | break;
35 | case z.ZodFirstPartyTypeKind.ZodVoid:
36 | break;
37 | case z.ZodFirstPartyTypeKind.ZodArray:
38 | break;
39 | case z.ZodFirstPartyTypeKind.ZodObject:
40 | break;
41 | case z.ZodFirstPartyTypeKind.ZodUnion:
42 | break;
43 | case z.ZodFirstPartyTypeKind.ZodDiscriminatedUnion:
44 | break;
45 | case z.ZodFirstPartyTypeKind.ZodIntersection:
46 | break;
47 | case z.ZodFirstPartyTypeKind.ZodTuple:
48 | break;
49 | case z.ZodFirstPartyTypeKind.ZodRecord:
50 | break;
51 | case z.ZodFirstPartyTypeKind.ZodMap:
52 | break;
53 | case z.ZodFirstPartyTypeKind.ZodSet:
54 | break;
55 | case z.ZodFirstPartyTypeKind.ZodFunction:
56 | break;
57 | case z.ZodFirstPartyTypeKind.ZodLazy:
58 | break;
59 | case z.ZodFirstPartyTypeKind.ZodLiteral:
60 | break;
61 | case z.ZodFirstPartyTypeKind.ZodEnum:
62 | break;
63 | case z.ZodFirstPartyTypeKind.ZodEffects:
64 | break;
65 | case z.ZodFirstPartyTypeKind.ZodNativeEnum:
66 | break;
67 | case z.ZodFirstPartyTypeKind.ZodOptional:
68 | break;
69 | case z.ZodFirstPartyTypeKind.ZodNullable:
70 | break;
71 | case z.ZodFirstPartyTypeKind.ZodDefault:
72 | break;
73 | case z.ZodFirstPartyTypeKind.ZodCatch:
74 | break;
75 | case z.ZodFirstPartyTypeKind.ZodPromise:
76 | break;
77 | case z.ZodFirstPartyTypeKind.ZodBranded:
78 | break;
79 | case z.ZodFirstPartyTypeKind.ZodPipeline:
80 | break;
81 | case z.ZodFirstPartyTypeKind.ZodSymbol:
82 | break;
83 | case z.ZodFirstPartyTypeKind.ZodReadonly:
84 | break;
85 | default:
86 | util.assertNever(def);
87 | }
88 | });
89 |
--------------------------------------------------------------------------------
/src/__tests__/standard-schema.test.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore TS6133
2 | import { expect, test } from "@jest/globals";
3 | import type { StandardSchemaV1 } from "@standard-schema/spec";
4 |
5 | import { util } from "../helpers/util";
6 | import * as z from "../index";
7 |
8 | test("assignability", () => {
9 | const _s1: StandardSchemaV1 = z.string();
10 | const _s2: StandardSchemaV1 = z.string();
11 | const _s3: StandardSchemaV1 = z.string();
12 | const _s4: StandardSchemaV1 = z.string();
13 | [_s1, _s2, _s3, _s4];
14 | });
15 |
16 | test("type inference", () => {
17 | const stringToNumber = z.string().transform((x) => x.length);
18 | type input = StandardSchemaV1.InferInput;
19 | util.assertEqual(true);
20 | type output = StandardSchemaV1.InferOutput;
21 | util.assertEqual