diff --git a/README.md b/README.md index 75048bbfc..40b5dec3d 100644 --- a/README.md +++ b/README.md @@ -946,6 +946,42 @@ const deepPartialUser = user.deepPartial(); > Important limitation: deep partials only work as expected in hierarchies of objects, arrays, and tuples. + +### `.required` + +Contrary to the `.partial` method, the `.required` method makes all properties required. + +Starting from this object: + +```ts +const user = z.object({ + email: z.string() + username: z.string(), +}).partial(); +// { email?: string | undefined; username?: string | undefined } +``` + +We can create a required version: + +```ts +const requiredUser = user.required(); +// { email: string; username: string } +``` + +You can also specify which properties to make required: + +```ts +const requiredEmail = user.required({ + email: true, +}); +/* +{ + email: string; + username?: string | undefined; +} +*/ +``` + ### `.passthrough` By default Zod object schemas strip out unrecognized keys during parsing. diff --git a/deno/lib/README.md b/deno/lib/README.md index 75048bbfc..40b5dec3d 100644 --- a/deno/lib/README.md +++ b/deno/lib/README.md @@ -946,6 +946,42 @@ const deepPartialUser = user.deepPartial(); > Important limitation: deep partials only work as expected in hierarchies of objects, arrays, and tuples. + +### `.required` + +Contrary to the `.partial` method, the `.required` method makes all properties required. + +Starting from this object: + +```ts +const user = z.object({ + email: z.string() + username: z.string(), +}).partial(); +// { email?: string | undefined; username?: string | undefined } +``` + +We can create a required version: + +```ts +const requiredUser = user.required(); +// { email: string; username: string } +``` + +You can also specify which properties to make required: + +```ts +const requiredEmail = user.required({ + email: true, +}); +/* +{ + email: string; + username?: string | undefined; +} +*/ +``` + ### `.passthrough` By default Zod object schemas strip out unrecognized keys during parsing. diff --git a/deno/lib/__tests__/partials.test.ts b/deno/lib/__tests__/partials.test.ts index 95983ad70..a739dc965 100644 --- a/deno/lib/__tests__/partials.test.ts +++ b/deno/lib/__tests__/partials.test.ts @@ -144,7 +144,22 @@ test("required", () => { expect(requiredObject.shape.field).toBeInstanceOf(z.ZodDefault); }); -test("with mask", async () => { +test("required with mask", () => { + const object = z.object({ + name: z.string(), + age: z.number().optional(), + field: z.string().optional().default("asdf"), + country: z.string().optional(), + }); + + const requiredObject = object.required({ age: true }); + expect(requiredObject.shape.name).toBeInstanceOf(z.ZodString); + expect(requiredObject.shape.age).toBeInstanceOf(z.ZodNumber); + expect(requiredObject.shape.field).toBeInstanceOf(z.ZodDefault); + expect(requiredObject.shape.country).toBeInstanceOf(z.ZodOptional); +}); + +test("partial with mask", async () => { const object = z.object({ name: z.string(), age: z.number().optional(), diff --git a/deno/lib/types.ts b/deno/lib/types.ts index 318bbc4dd..aeeb4d2d5 100644 --- a/deno/lib/types.ts +++ b/deno/lib/types.ts @@ -1931,16 +1931,41 @@ export class ZodObject< { [k in keyof T]: deoptional }, UnknownKeys, Catchall - > { + >; + required( + mask: Mask + ): ZodObject< + objectUtil.noNever<{ + [k in keyof T]: k extends keyof Mask ? deoptional : T[k]; + }>, + UnknownKeys, + Catchall + >; + required(mask?: any) { const newShape: any = {}; - for (const key in this.shape) { - const fieldSchema = this.shape[key]; - let newField = fieldSchema; - while (newField instanceof ZodOptional) { - newField = (newField as ZodOptional)._def.innerType; - } + if (mask) { + util.objectKeys(this.shape).map((key) => { + if (util.objectKeys(mask).indexOf(key) === -1) { + newShape[key] = this.shape[key]; + } else { + const fieldSchema = this.shape[key]; + let newField = fieldSchema; + while (newField instanceof ZodOptional) { + newField = (newField as ZodOptional)._def.innerType; + } + newShape[key] = newField; + } + }); + } else { + for (const key in this.shape) { + const fieldSchema = this.shape[key]; + let newField = fieldSchema; + while (newField instanceof ZodOptional) { + newField = (newField as ZodOptional)._def.innerType; + } - newShape[key] = newField; + newShape[key] = newField; + } } return new ZodObject({ ...this._def, diff --git a/src/__tests__/partials.test.ts b/src/__tests__/partials.test.ts index f2792286c..e06def866 100644 --- a/src/__tests__/partials.test.ts +++ b/src/__tests__/partials.test.ts @@ -143,7 +143,22 @@ test("required", () => { expect(requiredObject.shape.field).toBeInstanceOf(z.ZodDefault); }); -test("with mask", async () => { +test("required with mask", () => { + const object = z.object({ + name: z.string(), + age: z.number().optional(), + field: z.string().optional().default("asdf"), + country: z.string().optional(), + }); + + const requiredObject = object.required({ age: true }); + expect(requiredObject.shape.name).toBeInstanceOf(z.ZodString); + expect(requiredObject.shape.age).toBeInstanceOf(z.ZodNumber); + expect(requiredObject.shape.field).toBeInstanceOf(z.ZodDefault); + expect(requiredObject.shape.country).toBeInstanceOf(z.ZodOptional); +}); + +test("partial with mask", async () => { const object = z.object({ name: z.string(), age: z.number().optional(), diff --git a/src/types.ts b/src/types.ts index 73b5f6d19..b9935121a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1931,16 +1931,41 @@ export class ZodObject< { [k in keyof T]: deoptional }, UnknownKeys, Catchall - > { + >; + required( + mask: Mask + ): ZodObject< + objectUtil.noNever<{ + [k in keyof T]: k extends keyof Mask ? deoptional : T[k]; + }>, + UnknownKeys, + Catchall + >; + required(mask?: any) { const newShape: any = {}; - for (const key in this.shape) { - const fieldSchema = this.shape[key]; - let newField = fieldSchema; - while (newField instanceof ZodOptional) { - newField = (newField as ZodOptional)._def.innerType; - } + if (mask) { + util.objectKeys(this.shape).map((key) => { + if (util.objectKeys(mask).indexOf(key) === -1) { + newShape[key] = this.shape[key]; + } else { + const fieldSchema = this.shape[key]; + let newField = fieldSchema; + while (newField instanceof ZodOptional) { + newField = (newField as ZodOptional)._def.innerType; + } + newShape[key] = newField; + } + }); + } else { + for (const key in this.shape) { + const fieldSchema = this.shape[key]; + let newField = fieldSchema; + while (newField instanceof ZodOptional) { + newField = (newField as ZodOptional)._def.innerType; + } - newShape[key] = newField; + newShape[key] = newField; + } } return new ZodObject({ ...this._def,