diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 23121c90e..0e6e5c217 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,6 @@ jobs: - run: yarn build - run: yarn test - test-deno: runs-on: ubuntu-latest strategy: diff --git a/README.md b/README.md index ce0bf26c0..d913ed9b4 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,8 @@ - [`.or`](#or) - [`.and`](#and) - [`.brand`](#brand) - - [`.pipe()`](#pipe) + - [`.readonly`](#readonly) + - [`.pipe`](#pipe) - [You can use `.pipe()` to fix common issues with `z.coerce`.](#you-can-use-pipe-to-fix-common-issues-with-zcoerce) - [Guides and concepts](#guides-and-concepts) - [Type inference](#type-inference) @@ -1270,10 +1271,12 @@ Contrary to the `.partial` method, the `.required` method makes all properties r Starting from this object: ```ts -const user = z.object({ - email: z.string(), - username: z.string(), -}).partial(); +const user = z + .object({ + email: z.string(), + username: z.string(), + }) + .partial(); // { email?: string | undefined; username?: string | undefined } ``` @@ -2451,7 +2454,38 @@ type Cat = z.infer; Note that branded types do not affect the runtime result of `.parse`. It is a static-only construct. -### `.pipe()` +### `.readonly` + +`.readonly() => ZodReadonly` + +This method returns a `ZodReadonly` schema instance that parses the input using the base schema, then calls `Object.freeze()` on the result. The inferred type is also marked as `readonly`. + +```ts +const schema = z.object({ name: string }).readonly(); +type schema = z.infer; +// Readonly<{name: string}> + +const result = schema.parse({ name: "fido" }); +result.name = "simba"; // error +``` + +The inferred type uses TypeScript's built-in readonly types when relevant. + +```ts +z.array(z.string()).readonly(); +// readonly string[] + +z.tuple([z.string(), z.number()]).readonly(); +// readonly [string, number] + +z.map(z.string(), z.date()).readonly(); +// ReadonlyMap + +z.set(z.string()).readonly(); +// ReadonlySet> +``` + +### `.pipe` Schemas can be chained into validation "pipelines". It's useful for easily validating the result after a `.transform()`: diff --git a/deno/lib/README.md b/deno/lib/README.md index 3e2ecfd23..d913ed9b4 100644 --- a/deno/lib/README.md +++ b/deno/lib/README.md @@ -143,7 +143,8 @@ - [`.or`](#or) - [`.and`](#and) - [`.brand`](#brand) - - [`.pipe()`](#pipe) + - [`.readonly`](#readonly) + - [`.pipe`](#pipe) - [You can use `.pipe()` to fix common issues with `z.coerce`.](#you-can-use-pipe-to-fix-common-issues-with-zcoerce) - [Guides and concepts](#guides-and-concepts) - [Type inference](#type-inference) @@ -460,6 +461,7 @@ There are a growing number of tools that are built atop or support Zod natively! #### Form integrations +- [`conform`](https://conform.guide/api/zod): A progressive enhancement first form validation library for Remix and React Router - [`react-hook-form`](https://github.com/react-hook-form/resolvers#zod): A first-party Zod resolver for React Hook Form. - [`zod-validation-error`](https://github.com/causaly/zod-validation-error): Generate user-friendly error messages from `ZodError`s. - [`zod-formik-adapter`](https://github.com/robertLichtnow/zod-formik-adapter): A community-maintained Formik adapter for Zod. @@ -471,7 +473,8 @@ There are a growing number of tools that are built atop or support Zod natively! - [`@modular-forms/solid`](https://github.com/fabian-hiller/modular-forms): Modular form library for SolidJS that supports Zod for validation. - [`houseform`](https://github.com/crutchcorn/houseform/): A React form library that uses Zod for validation. - [`sveltekit-superforms`](https://github.com/ciscoheat/sveltekit-superforms): Supercharged form library for SvelteKit with Zod validation. -- [`mobx-zod-form`](https://github.com/MonoidDev/mobx-zod-form): Data-first form builder based on MobX & Zod +- [`mobx-zod-form`](https://github.com/MonoidDev/mobx-zod-form): Data-first form builder based on MobX & Zod. +- [`@vee-validate/zod`](https://github.com/logaretm/vee-validate/tree/main/packages/zod): Form library for Vue.js with Zod schema validation. #### Zod to X @@ -483,7 +486,8 @@ There are a growing number of tools that are built atop or support Zod natively! - [`fastify-type-provider-zod`](https://github.com/turkerdev/fastify-type-provider-zod): Create Fastify type providers from Zod schemas. - [`zod-to-openapi`](https://github.com/asteasolutions/zod-to-openapi): Generate full OpenAPI (Swagger) docs from Zod, including schemas, endpoints & parameters. - [`nestjs-graphql-zod`](https://github.com/incetarik/nestjs-graphql-zod): Generates NestJS GraphQL model classes from Zod schemas. Provides GraphQL method decorators working with Zod schemas. -- [`zod-openapi`](https://github.com/samchungy/zod-openapi): Create full OpenAPI v3.x documentation from Zod Schemas. +- [`zod-openapi`](https://github.com/samchungy/zod-openapi): Create full OpenAPI v3.x documentation from Zod schemas. +- [`fastify-zod-openapi`](https://github.com/samchungy/fastify-zod-openapi): Fastify type provider, validation, serialization and @fastify/swagger support for Zod schemas. #### X to Zod @@ -1203,7 +1207,7 @@ Starting from this object: ```ts const user = z.object({ - email: z.string() + email: z.string(), username: z.string(), }); // { email: string; username: string } @@ -1267,10 +1271,12 @@ Contrary to the `.partial` method, the `.required` method makes all properties r Starting from this object: ```ts -const user = z.object({ - email: z.string() - username: z.string(), -}).partial(); +const user = z + .object({ + email: z.string(), + username: z.string(), + }) + .partial(); // { email?: string | undefined; username?: string | undefined } ``` @@ -2448,7 +2454,38 @@ type Cat = z.infer; Note that branded types do not affect the runtime result of `.parse`. It is a static-only construct. -### `.pipe()` +### `.readonly` + +`.readonly() => ZodReadonly` + +This method returns a `ZodReadonly` schema instance that parses the input using the base schema, then calls `Object.freeze()` on the result. The inferred type is also marked as `readonly`. + +```ts +const schema = z.object({ name: string }).readonly(); +type schema = z.infer; +// Readonly<{name: string}> + +const result = schema.parse({ name: "fido" }); +result.name = "simba"; // error +``` + +The inferred type uses TypeScript's built-in readonly types when relevant. + +```ts +z.array(z.string()).readonly(); +// readonly string[] + +z.tuple([z.string(), z.number()]).readonly(); +// readonly [string, number] + +z.map(z.string(), z.date()).readonly(); +// ReadonlyMap + +z.set(z.string()).readonly(); +// ReadonlySet> +``` + +### `.pipe` Schemas can be chained into validation "pipelines". It's useful for easily validating the result after a `.transform()`: @@ -2724,7 +2761,6 @@ Yup is a full-featured library that was implemented first in vanilla JS, and lat - Supports casting and transforms - All object fields are optional by default -- Missing object methods: (partial, deepPartial) - Missing promise schemas - Missing function schemas @@ -2787,7 +2823,7 @@ This more declarative API makes schema definitions vastly more concise. [https://github.com/pelotom/runtypes](https://github.com/pelotom/runtypes) -Good type inference support, but limited options for object type masking (no `.pick` , `.omit` , `.extend` , etc.). No support for `Record` s (their `Record` is equivalent to Zod's `object` ). They DO support readonly types, which Zod does not. +Good type inference support. They DO support readonly types, which Zod does not. - Supports "pattern matching": computed properties that distribute over unions - Supports readonly types diff --git a/deno/lib/__tests__/readonly.test.ts b/deno/lib/__tests__/readonly.test.ts new file mode 100644 index 000000000..27d2e0c9c --- /dev/null +++ b/deno/lib/__tests__/readonly.test.ts @@ -0,0 +1,205 @@ +// @ts-ignore TS6133 +import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts"; +const test = Deno.test; + +import { util } from "../helpers/util.ts"; +import * as z from "../index.ts"; + +enum testEnum { + A, + B, +} + +const schemas = [ + z.string().readonly(), + z.number().readonly(), + z.nan().readonly(), + z.bigint().readonly(), + z.boolean().readonly(), + z.date().readonly(), + z.undefined().readonly(), + z.null().readonly(), + z.any().readonly(), + z.unknown().readonly(), + z.void().readonly(), + z.function().args(z.string(), z.number()).readonly(), + + z.array(z.string()).readonly(), + z.tuple([z.string(), z.number()]).readonly(), + z.map(z.string(), z.date()).readonly(), + z.set(z.promise(z.string())).readonly(), + z.record(z.string()).readonly(), + z.record(z.string(), z.number()).readonly(), + z.object({ a: z.string(), 1: z.number() }).readonly(), + z.nativeEnum(testEnum).readonly(), + z.promise(z.string()).readonly(), +] as const; + +test("flat inference", () => { + util.assertEqual, string>(true); + util.assertEqual, number>(true); + util.assertEqual, number>(true); + util.assertEqual, bigint>(true); + util.assertEqual, boolean>(true); + util.assertEqual, Date>(true); + util.assertEqual, undefined>(true); + util.assertEqual, null>(true); + util.assertEqual, any>(true); + util.assertEqual, Readonly>(true); + util.assertEqual, void>(true); + util.assertEqual< + z.infer<(typeof schemas)[11]>, + (args_0: string, args_1: number, ...args_2: unknown[]) => unknown + >(true); + util.assertEqual, readonly string[]>(true); + + util.assertEqual, readonly [string, number]>( + true + ); + util.assertEqual, ReadonlyMap>( + true + ); + util.assertEqual, ReadonlySet>>( + true + ); + util.assertEqual< + z.infer<(typeof schemas)[16]>, + Readonly> + >(true); + util.assertEqual< + z.infer<(typeof schemas)[17]>, + Readonly> + >(true); + util.assertEqual< + z.infer<(typeof schemas)[18]>, + { readonly a: string; readonly 1: number } + >(true); + util.assertEqual, Readonly>(true); + util.assertEqual, Promise>(true); +}); + +// test("deep inference", () => { +// util.assertEqual, string>(true); +// util.assertEqual, number>(true); +// util.assertEqual, number>(true); +// util.assertEqual, bigint>(true); +// util.assertEqual, boolean>(true); +// util.assertEqual, Date>(true); +// util.assertEqual, undefined>(true); +// util.assertEqual, null>(true); +// util.assertEqual, any>(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[9]>, +// Readonly +// >(true); +// util.assertEqual, void>(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[11]>, +// (args_0: string, args_1: number, ...args_2: unknown[]) => unknown +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[12]>, +// readonly string[] +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[13]>, +// readonly [string, number] +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[14]>, +// ReadonlyMap +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[15]>, +// ReadonlySet> +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[16]>, +// Readonly> +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[17]>, +// Readonly> +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[18]>, +// { readonly a: string; readonly 1: number } +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[19]>, +// Readonly +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[20]>, +// Promise +// >(true); + +// util.assertEqual< +// z.infer, +// ReadonlyMap< +// ReadonlySet, +// { +// readonly a: { +// readonly [x: string]: readonly any[]; +// }; +// readonly b: { +// readonly c: { +// readonly d: { +// readonly e: { +// readonly f: { +// readonly g?: {}; +// }; +// }; +// }; +// }; +// }; +// } +// > +// >(true); +// }); + +test("object freezing", () => { + expect(Object.isFrozen(z.array(z.string()).readonly().parse(["a"]))).toBe( + true + ); + expect( + Object.isFrozen( + z.tuple([z.string(), z.number()]).readonly().parse(["a", 1]) + ) + ).toBe(true); + expect( + Object.isFrozen( + z + .map(z.string(), z.date()) + .readonly() + .parse(new Map([["a", new Date()]])) + ) + ).toBe(true); + expect( + Object.isFrozen( + z + .set(z.promise(z.string())) + .readonly() + .parse(new Set([Promise.resolve("a")])) + ) + ).toBe(true); + expect( + Object.isFrozen(z.record(z.string()).readonly().parse({ a: "b" })) + ).toBe(true); + expect( + Object.isFrozen(z.record(z.string(), z.number()).readonly().parse({ a: 1 })) + ).toBe(true); + expect( + Object.isFrozen( + z + .object({ a: z.string(), 1: z.number() }) + .readonly() + .parse({ a: "b", 1: 2 }) + ) + ).toBe(true); + expect( + Object.isFrozen( + z.promise(z.string()).readonly().parse(Promise.resolve("a")) + ) + ).toBe(true); +}); diff --git a/deno/lib/types.ts b/deno/lib/types.ts index 13d42a738..0740927fa 100644 --- a/deno/lib/types.ts +++ b/deno/lib/types.ts @@ -402,6 +402,7 @@ export abstract class ZodType< this.catch = this.catch.bind(this); this.describe = this.describe.bind(this); this.pipe = this.pipe.bind(this); + this.readonly = this.readonly.bind(this); this.isNullable = this.isNullable.bind(this); this.isOptional = this.isOptional.bind(this); } @@ -489,6 +490,9 @@ export abstract class ZodType< pipe(target: T): ZodPipeline { return ZodPipeline.create(this, target); } + readonly(): ZodReadonly { + return ZodReadonly.create(this); + } isOptional(): boolean { return this.safeParse(undefined).success; @@ -4768,6 +4772,72 @@ export class ZodPipeline< } } +/////////////////////////////////////////// +/////////////////////////////////////////// +////////// ////////// +////////// ZodReadonly ////////// +////////// ////////// +/////////////////////////////////////////// +/////////////////////////////////////////// +type BuiltIn = + | (((...args: any[]) => any) | (new (...args: any[]) => any)) + | { readonly [Symbol.toStringTag]: string } + | Date + | Error + | Generator + | Promise + | RegExp; + +type MakeReadonly = T extends Map + ? ReadonlyMap + : T extends Set + ? ReadonlySet + : T extends [infer Head, ...infer Tail] + ? readonly [Head, ...Tail] + : T extends Array + ? ReadonlyArray + : T extends BuiltIn + ? T + : Readonly; + +export interface ZodReadonlyDef + extends ZodTypeDef { + innerType: T; + typeName: ZodFirstPartyTypeKind.ZodReadonly; +} + +export class ZodReadonly extends ZodType< + MakeReadonly, + ZodReadonlyDef, + T["_input"] +> { + _parse(input: ParseInput): ParseReturnType { + const result = this._def.innerType._parse(input); + if (isValid(result)) { + result.value = Object.freeze(result.value); + } + return result; + } + + static create = ( + type: T, + params?: RawCreateParams + ): ZodReadonly => { + return new ZodReadonly({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodReadonly, + ...processCreateParams(params), + }) as any; + }; +} + +//////////////////////////////////////// +//////////////////////////////////////// +////////// ////////// +////////// z.custom ////////// +////////// ////////// +//////////////////////////////////////// +//////////////////////////////////////// type CustomParams = CustomErrorParams & { fatal?: boolean }; export const custom = ( check?: (data: unknown) => any, @@ -4843,6 +4913,7 @@ export enum ZodFirstPartyTypeKind { ZodPromise = "ZodPromise", ZodBranded = "ZodBranded", ZodPipeline = "ZodPipeline", + ZodReadonly = "ZodReadonly", } export type ZodFirstPartySchemaTypes = | ZodString diff --git a/playground.ts b/playground.ts index b12a86fd6..4e01473b6 100644 --- a/playground.ts +++ b/playground.ts @@ -1,8 +1,3 @@ import { z } from "./src"; -z; -const schema = z.coerce.date(); -// console.log(schema.parse("3.14")); -test("asdf", () => { - expect(schema.parse("3.14")).toBeInstanceOf(Date); -}); +z; diff --git a/src/__tests__/readonly.test.ts b/src/__tests__/readonly.test.ts new file mode 100644 index 000000000..3007a6fac --- /dev/null +++ b/src/__tests__/readonly.test.ts @@ -0,0 +1,204 @@ +// @ts-ignore TS6133 +import { test, expect } from "@jest/globals"; + +import { util } from "../helpers/util"; +import * as z from "../index"; + +enum testEnum { + A, + B, +} + +const schemas = [ + z.string().readonly(), + z.number().readonly(), + z.nan().readonly(), + z.bigint().readonly(), + z.boolean().readonly(), + z.date().readonly(), + z.undefined().readonly(), + z.null().readonly(), + z.any().readonly(), + z.unknown().readonly(), + z.void().readonly(), + z.function().args(z.string(), z.number()).readonly(), + + z.array(z.string()).readonly(), + z.tuple([z.string(), z.number()]).readonly(), + z.map(z.string(), z.date()).readonly(), + z.set(z.promise(z.string())).readonly(), + z.record(z.string()).readonly(), + z.record(z.string(), z.number()).readonly(), + z.object({ a: z.string(), 1: z.number() }).readonly(), + z.nativeEnum(testEnum).readonly(), + z.promise(z.string()).readonly(), +] as const; + +test("flat inference", () => { + util.assertEqual, string>(true); + util.assertEqual, number>(true); + util.assertEqual, number>(true); + util.assertEqual, bigint>(true); + util.assertEqual, boolean>(true); + util.assertEqual, Date>(true); + util.assertEqual, undefined>(true); + util.assertEqual, null>(true); + util.assertEqual, any>(true); + util.assertEqual, Readonly>(true); + util.assertEqual, void>(true); + util.assertEqual< + z.infer<(typeof schemas)[11]>, + (args_0: string, args_1: number, ...args_2: unknown[]) => unknown + >(true); + util.assertEqual, readonly string[]>(true); + + util.assertEqual, readonly [string, number]>( + true + ); + util.assertEqual, ReadonlyMap>( + true + ); + util.assertEqual, ReadonlySet>>( + true + ); + util.assertEqual< + z.infer<(typeof schemas)[16]>, + Readonly> + >(true); + util.assertEqual< + z.infer<(typeof schemas)[17]>, + Readonly> + >(true); + util.assertEqual< + z.infer<(typeof schemas)[18]>, + { readonly a: string; readonly 1: number } + >(true); + util.assertEqual, Readonly>(true); + util.assertEqual, Promise>(true); +}); + +// test("deep inference", () => { +// util.assertEqual, string>(true); +// util.assertEqual, number>(true); +// util.assertEqual, number>(true); +// util.assertEqual, bigint>(true); +// util.assertEqual, boolean>(true); +// util.assertEqual, Date>(true); +// util.assertEqual, undefined>(true); +// util.assertEqual, null>(true); +// util.assertEqual, any>(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[9]>, +// Readonly +// >(true); +// util.assertEqual, void>(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[11]>, +// (args_0: string, args_1: number, ...args_2: unknown[]) => unknown +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[12]>, +// readonly string[] +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[13]>, +// readonly [string, number] +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[14]>, +// ReadonlyMap +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[15]>, +// ReadonlySet> +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[16]>, +// Readonly> +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[17]>, +// Readonly> +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[18]>, +// { readonly a: string; readonly 1: number } +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[19]>, +// Readonly +// >(true); +// util.assertEqual< +// z.infer<(typeof deepReadonlySchemas_0)[20]>, +// Promise +// >(true); + +// util.assertEqual< +// z.infer, +// ReadonlyMap< +// ReadonlySet, +// { +// readonly a: { +// readonly [x: string]: readonly any[]; +// }; +// readonly b: { +// readonly c: { +// readonly d: { +// readonly e: { +// readonly f: { +// readonly g?: {}; +// }; +// }; +// }; +// }; +// }; +// } +// > +// >(true); +// }); + +test("object freezing", () => { + expect(Object.isFrozen(z.array(z.string()).readonly().parse(["a"]))).toBe( + true + ); + expect( + Object.isFrozen( + z.tuple([z.string(), z.number()]).readonly().parse(["a", 1]) + ) + ).toBe(true); + expect( + Object.isFrozen( + z + .map(z.string(), z.date()) + .readonly() + .parse(new Map([["a", new Date()]])) + ) + ).toBe(true); + expect( + Object.isFrozen( + z + .set(z.promise(z.string())) + .readonly() + .parse(new Set([Promise.resolve("a")])) + ) + ).toBe(true); + expect( + Object.isFrozen(z.record(z.string()).readonly().parse({ a: "b" })) + ).toBe(true); + expect( + Object.isFrozen(z.record(z.string(), z.number()).readonly().parse({ a: 1 })) + ).toBe(true); + expect( + Object.isFrozen( + z + .object({ a: z.string(), 1: z.number() }) + .readonly() + .parse({ a: "b", 1: 2 }) + ) + ).toBe(true); + expect( + Object.isFrozen( + z.promise(z.string()).readonly().parse(Promise.resolve("a")) + ) + ).toBe(true); +}); diff --git a/src/types.ts b/src/types.ts index eb4349c25..81a5e9f8a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -402,6 +402,7 @@ export abstract class ZodType< this.catch = this.catch.bind(this); this.describe = this.describe.bind(this); this.pipe = this.pipe.bind(this); + this.readonly = this.readonly.bind(this); this.isNullable = this.isNullable.bind(this); this.isOptional = this.isOptional.bind(this); } @@ -489,6 +490,9 @@ export abstract class ZodType< pipe(target: T): ZodPipeline { return ZodPipeline.create(this, target); } + readonly(): ZodReadonly { + return ZodReadonly.create(this); + } isOptional(): boolean { return this.safeParse(undefined).success; @@ -4768,6 +4772,72 @@ export class ZodPipeline< } } +/////////////////////////////////////////// +/////////////////////////////////////////// +////////// ////////// +////////// ZodReadonly ////////// +////////// ////////// +/////////////////////////////////////////// +/////////////////////////////////////////// +type BuiltIn = + | (((...args: any[]) => any) | (new (...args: any[]) => any)) + | { readonly [Symbol.toStringTag]: string } + | Date + | Error + | Generator + | Promise + | RegExp; + +type MakeReadonly = T extends Map + ? ReadonlyMap + : T extends Set + ? ReadonlySet + : T extends [infer Head, ...infer Tail] + ? readonly [Head, ...Tail] + : T extends Array + ? ReadonlyArray + : T extends BuiltIn + ? T + : Readonly; + +export interface ZodReadonlyDef + extends ZodTypeDef { + innerType: T; + typeName: ZodFirstPartyTypeKind.ZodReadonly; +} + +export class ZodReadonly extends ZodType< + MakeReadonly, + ZodReadonlyDef, + T["_input"] +> { + _parse(input: ParseInput): ParseReturnType { + const result = this._def.innerType._parse(input); + if (isValid(result)) { + result.value = Object.freeze(result.value); + } + return result; + } + + static create = ( + type: T, + params?: RawCreateParams + ): ZodReadonly => { + return new ZodReadonly({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodReadonly, + ...processCreateParams(params), + }) as any; + }; +} + +//////////////////////////////////////// +//////////////////////////////////////// +////////// ////////// +////////// z.custom ////////// +////////// ////////// +//////////////////////////////////////// +//////////////////////////////////////// type CustomParams = CustomErrorParams & { fatal?: boolean }; export const custom = ( check?: (data: unknown) => any, @@ -4843,6 +4913,7 @@ export enum ZodFirstPartyTypeKind { ZodPromise = "ZodPromise", ZodBranded = "ZodBranded", ZodPipeline = "ZodPipeline", + ZodReadonly = "ZodReadonly", } export type ZodFirstPartySchemaTypes = | ZodString diff --git a/tsconfig.json b/tsconfig.json index a1b3dc7e4..aead72af7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,3 +1,6 @@ { - "extends": "./configs/tsconfig.base.json" + "extends": "./configs/tsconfig.base.json", + "compilerOptions": { + "outDir": "../lib" + } }