Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

deny unexpected keys @ ZodObject's .omit(mask),.pick(mask),.required(mask) & .partial(mask) at compile time. #1564

Merged
5 changes: 3 additions & 2 deletions deno/lib/__tests__/pickomit.test.ts
Expand Up @@ -66,13 +66,13 @@ test("nonstrict inference", () => {
});

test("nonstrict parsing - pass", () => {
const laxfish = fish.nonstrict().pick({ name: true });
const laxfish = fish.passthrough().pick({ name: true });
laxfish.parse({ name: "asdf", whatever: "asdf" });
laxfish.parse({ name: "asdf", age: 12, nested: {} });
});

test("nonstrict parsing - fail", () => {
const laxfish = fish.nonstrict().pick({ name: true });
const laxfish = fish.passthrough().pick({ name: true });
const bad = () => laxfish.parse({ whatever: "asdf" } as any);
expect(bad).toThrow();
});
Expand All @@ -85,6 +85,7 @@ test("pick a nonexistent key", () => {

const pickedSchema = schema.pick({
a: true,
// @ts-expect-error
doesntExist: true,
igalklebanov marked this conversation as resolved.
Show resolved Hide resolved
});

Expand Down
14 changes: 10 additions & 4 deletions deno/lib/types.ts
Expand Up @@ -1772,6 +1772,12 @@ export type SomeZodObject = ZodObject<
any
>;

export type objectKeyMask<Obj> = { [k in keyof Obj]?: true };

export type optionalPickWith<Obj, Shape> = {
[k in keyof Obj]?: k extends keyof Shape ? Obj[k] : never;
};

function deepPartialify(schema: ZodTypeAny): any {
if (schema instanceof ZodObject) {
const newShape: any = {};
Expand Down Expand Up @@ -2015,8 +2021,8 @@ export class ZodObject<
}) as any;
}

pick<Mask extends { [k in keyof T]?: true }>(
mask: Mask
pick<Mask extends objectKeyMask<T>>(
mask: optionalPickWith<Mask, objectKeyMask<T>>
): ZodObject<Pick<T, Extract<keyof T, keyof Mask>>, UnknownKeys, Catchall> {
const shape: any = {};
util.objectKeys(mask).map((key) => {
Expand All @@ -2029,8 +2035,8 @@ export class ZodObject<
}) as any;
}

omit<Mask extends { [k in keyof T]?: true }>(
mask: Mask
omit<Mask extends objectKeyMask<T>>(
mask: optionalPickWith<Mask, objectKeyMask<T>>
): ZodObject<Omit<T, keyof Mask>, UnknownKeys, Catchall> {
const shape: any = {};
util.objectKeys(this.shape).map((key) => {
Expand Down
5 changes: 3 additions & 2 deletions src/__tests__/pickomit.test.ts
Expand Up @@ -65,13 +65,13 @@ test("nonstrict inference", () => {
});

test("nonstrict parsing - pass", () => {
const laxfish = fish.nonstrict().pick({ name: true });
const laxfish = fish.passthrough().pick({ name: true });
igalklebanov marked this conversation as resolved.
Show resolved Hide resolved
laxfish.parse({ name: "asdf", whatever: "asdf" });
laxfish.parse({ name: "asdf", age: 12, nested: {} });
});

test("nonstrict parsing - fail", () => {
const laxfish = fish.nonstrict().pick({ name: true });
const laxfish = fish.passthrough().pick({ name: true });
const bad = () => laxfish.parse({ whatever: "asdf" } as any);
expect(bad).toThrow();
});
Expand All @@ -84,6 +84,7 @@ test("pick a nonexistent key", () => {

const pickedSchema = schema.pick({
a: true,
// @ts-expect-error
doesntExist: true,
});

Expand Down
14 changes: 10 additions & 4 deletions src/types.ts
Expand Up @@ -1772,6 +1772,12 @@ export type SomeZodObject = ZodObject<
any
>;

export type objectKeyMask<Obj> = { [k in keyof Obj]?: true };

export type optionalPickWith<Obj, Shape> = {
[k in keyof Obj]?: k extends keyof Shape ? Obj[k] : never;
};

function deepPartialify(schema: ZodTypeAny): any {
if (schema instanceof ZodObject) {
const newShape: any = {};
Expand Down Expand Up @@ -2015,8 +2021,8 @@ export class ZodObject<
}) as any;
}

pick<Mask extends { [k in keyof T]?: true }>(
mask: Mask
pick<Mask extends objectKeyMask<T>>(
mask: optionalPickWith<Mask, objectKeyMask<T>>
): ZodObject<Pick<T, Extract<keyof T, keyof Mask>>, UnknownKeys, Catchall> {
const shape: any = {};
util.objectKeys(mask).map((key) => {
Expand All @@ -2029,8 +2035,8 @@ export class ZodObject<
}) as any;
}

omit<Mask extends { [k in keyof T]?: true }>(
mask: Mask
omit<Mask extends objectKeyMask<T>>(
mask: optionalPickWith<Mask, objectKeyMask<T>>
): ZodObject<Omit<T, keyof Mask>, UnknownKeys, Catchall> {
const shape: any = {};
util.objectKeys(this.shape).map((key) => {
Expand Down