Skip to content

Commit 0269910

Browse files
authored
Improve addQuestionMarks, fix #2184 (#3352)
* Improve addQuestionMarks, fix #2184 * Fix #2196 * Fix * Add test case
1 parent b941914 commit 0269910

File tree

7 files changed

+93
-14
lines changed

7 files changed

+93
-14
lines changed

deno/lib/__tests__/generics.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,19 @@ test("generics", () => {
2323
const result = stripOuter(z.object({ a: z.string() }), { a: "asdf" });
2424
util.assertEqual<typeof result, Promise<{ a: string }>>(true);
2525
});
26+
27+
test("assignability", () => {
28+
const createSchemaAndParse = <K extends string, VS extends z.ZodString>(
29+
key: K,
30+
valueSchema: VS,
31+
data: unknown
32+
) => {
33+
const schema = z.object({
34+
[key]: valueSchema,
35+
});
36+
const parsed = schema.parse(data);
37+
const inferred: z.infer<z.ZodObject<{ [k in K]: VS }>> = parsed;
38+
return inferred;
39+
};
40+
createSchemaAndParse("foo", z.string(), { foo: "" });
41+
});

deno/lib/__tests__/object.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,3 +451,23 @@ test("passthrough index signature", () => {
451451
type b = z.infer<typeof b>;
452452
util.assertEqual<{ a: string } & { [k: string]: unknown }, b>(true);
453453
});
454+
455+
test("xor", () => {
456+
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
457+
type XOR<T, U> = T extends object
458+
? U extends object
459+
? (Without<T, U> & U) | (Without<U, T> & T)
460+
: U
461+
: T;
462+
463+
type A = { name: string; a: number };
464+
type B = { name: string; b: number };
465+
type C = XOR<A, B>;
466+
type Outer = { data: C };
467+
const Outer: z.ZodType<Outer> = z.object({
468+
data: z.union([
469+
z.object({ name: z.string(), a: z.number() }),
470+
z.object({ name: z.string(), b: z.number() }),
471+
]),
472+
});
473+
});

deno/lib/helpers/util.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,18 +100,23 @@ export namespace objectUtil {
100100
[k in Exclude<keyof U, keyof V>]: U[k];
101101
} & V;
102102

103-
// type optionalKeys<T extends object> = {
104-
// [k in keyof T]: undefined extends T[k] ? k : never;
105-
// }[keyof T];
103+
type optionalKeys<T extends object> = {
104+
[k in keyof T]: undefined extends T[k] ? k : never;
105+
}[keyof T];
106106

107107
type requiredKeys<T extends object> = {
108108
[k in keyof T]: undefined extends T[k] ? never : k;
109109
}[keyof T];
110110

111+
// export type addQuestionMarks<
112+
// T extends object,
113+
// R extends keyof T = requiredKeys<T>
114+
// > = Pick<Required<T>, R> & Partial<T>;
111115
export type addQuestionMarks<
112116
T extends object,
113-
R extends keyof T = requiredKeys<T>
114-
> = Pick<Required<T>, R> & Partial<T>;
117+
R extends keyof T = requiredKeys<T>,
118+
O extends keyof T = optionalKeys<T>
119+
> = Pick<T, R> & Partial<Pick<T, O>> & { [k in keyof T]?: unknown };
115120

116121
export type identity<T> = T;
117122
export type flatten<T> = identity<{ [k in keyof T]: T[k] }>;

playground.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import { z, ZodNativeEnum } from "./src";
1+
import { z } from "./src";
22

33
z;
4-
5-
const A = z.object({}).catchall(z.string());
6-
type A = z.infer<typeof A>;

src/__tests__/generics.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,19 @@ test("generics", () => {
2222
const result = stripOuter(z.object({ a: z.string() }), { a: "asdf" });
2323
util.assertEqual<typeof result, Promise<{ a: string }>>(true);
2424
});
25+
26+
test("assignability", () => {
27+
const createSchemaAndParse = <K extends string, VS extends z.ZodString>(
28+
key: K,
29+
valueSchema: VS,
30+
data: unknown
31+
) => {
32+
const schema = z.object({
33+
[key]: valueSchema,
34+
});
35+
const parsed = schema.parse(data);
36+
const inferred: z.infer<z.ZodObject<{ [k in K]: VS }>> = parsed;
37+
return inferred;
38+
};
39+
createSchemaAndParse("foo", z.string(), { foo: "" });
40+
});

src/__tests__/object.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,3 +450,23 @@ test("passthrough index signature", () => {
450450
type b = z.infer<typeof b>;
451451
util.assertEqual<{ a: string } & { [k: string]: unknown }, b>(true);
452452
});
453+
454+
test("xor", () => {
455+
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
456+
type XOR<T, U> = T extends object
457+
? U extends object
458+
? (Without<T, U> & U) | (Without<U, T> & T)
459+
: U
460+
: T;
461+
462+
type A = { name: string; a: number };
463+
type B = { name: string; b: number };
464+
type C = XOR<A, B>;
465+
type Outer = { data: C };
466+
const Outer: z.ZodType<Outer> = z.object({
467+
data: z.union([
468+
z.object({ name: z.string(), a: z.number() }),
469+
z.object({ name: z.string(), b: z.number() }),
470+
]),
471+
});
472+
});

src/helpers/util.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,18 +100,23 @@ export namespace objectUtil {
100100
[k in Exclude<keyof U, keyof V>]: U[k];
101101
} & V;
102102

103-
// type optionalKeys<T extends object> = {
104-
// [k in keyof T]: undefined extends T[k] ? k : never;
105-
// }[keyof T];
103+
type optionalKeys<T extends object> = {
104+
[k in keyof T]: undefined extends T[k] ? k : never;
105+
}[keyof T];
106106

107107
type requiredKeys<T extends object> = {
108108
[k in keyof T]: undefined extends T[k] ? never : k;
109109
}[keyof T];
110110

111+
// export type addQuestionMarks<
112+
// T extends object,
113+
// R extends keyof T = requiredKeys<T>
114+
// > = Pick<Required<T>, R> & Partial<T>;
111115
export type addQuestionMarks<
112116
T extends object,
113-
R extends keyof T = requiredKeys<T>
114-
> = Pick<Required<T>, R> & Partial<T>;
117+
R extends keyof T = requiredKeys<T>,
118+
O extends keyof T = optionalKeys<T>
119+
> = Pick<T, R> & Partial<Pick<T, O>> & { [k in keyof T]?: unknown };
115120

116121
export type identity<T> = T;
117122
export type flatten<T> = identity<{ [k in keyof T]: T[k] }>;

0 commit comments

Comments
 (0)