zod 4.2.0-canary.20251118T063547 → 4.2.0-canary.20251118T185426
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -302,6 +302,29 @@ test("z.record", () => {
|
|
|
302
302
|
const d = z.record(z.enum(["a", "b"]).or(z.never()), z.string());
|
|
303
303
|
type d = z.output<typeof d>;
|
|
304
304
|
expectTypeOf<d>().toEqualTypeOf<Record<"a" | "b", string>>();
|
|
305
|
+
|
|
306
|
+
// literal union keys
|
|
307
|
+
const e = z.record(z.union([z.literal("a"), z.literal(0)]), z.string());
|
|
308
|
+
type e = z.output<typeof e>;
|
|
309
|
+
expectTypeOf<e>().toEqualTypeOf<Record<"a" | 0, string>>();
|
|
310
|
+
expect(z.parse(e, { a: "hello", 0: "world" })).toEqual({
|
|
311
|
+
a: "hello",
|
|
312
|
+
0: "world",
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// TypeScript enum keys
|
|
316
|
+
enum Enum {
|
|
317
|
+
A = 0,
|
|
318
|
+
B = "hi",
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const f = z.record(z.enum(Enum), z.string());
|
|
322
|
+
type f = z.output<typeof f>;
|
|
323
|
+
expectTypeOf<f>().toEqualTypeOf<Record<Enum, string>>();
|
|
324
|
+
expect(z.parse(f, { [Enum.A]: "hello", [Enum.B]: "world" })).toEqual({
|
|
325
|
+
[Enum.A]: "hello",
|
|
326
|
+
[Enum.B]: "world",
|
|
327
|
+
});
|
|
305
328
|
});
|
|
306
329
|
|
|
307
330
|
test("z.map", () => {
|
|
@@ -8,16 +8,28 @@ test("type inference", () => {
|
|
|
8
8
|
const recordWithEnumKeys = z.record(z.enum(["Tuna", "Salmon"]), z.string());
|
|
9
9
|
type recordWithEnumKeys = z.infer<typeof recordWithEnumKeys>;
|
|
10
10
|
|
|
11
|
-
const recordWithLiteralKey = z.record(z.literal(["Tuna", "Salmon"]), z.string());
|
|
11
|
+
const recordWithLiteralKey = z.record(z.literal(["Tuna", "Salmon", 21]), z.string());
|
|
12
12
|
type recordWithLiteralKey = z.infer<typeof recordWithLiteralKey>;
|
|
13
13
|
|
|
14
|
-
const recordWithLiteralUnionKeys = z.record(
|
|
14
|
+
const recordWithLiteralUnionKeys = z.record(
|
|
15
|
+
z.union([z.literal("Tuna"), z.literal("Salmon"), z.literal(21)]),
|
|
16
|
+
z.string()
|
|
17
|
+
);
|
|
15
18
|
type recordWithLiteralUnionKeys = z.infer<typeof recordWithLiteralUnionKeys>;
|
|
16
19
|
|
|
20
|
+
enum Enum {
|
|
21
|
+
Tuna = 0,
|
|
22
|
+
Salmon = "Shark",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const recordWithTypescriptEnum = z.record(z.enum(Enum), z.string());
|
|
26
|
+
type recordWithTypescriptEnum = z.infer<typeof recordWithTypescriptEnum>;
|
|
27
|
+
|
|
17
28
|
expectTypeOf<booleanRecord>().toEqualTypeOf<Record<string, boolean>>();
|
|
18
29
|
expectTypeOf<recordWithEnumKeys>().toEqualTypeOf<Record<"Tuna" | "Salmon", string>>();
|
|
19
|
-
expectTypeOf<recordWithLiteralKey>().toEqualTypeOf<Record<"Tuna" | "Salmon", string>>();
|
|
20
|
-
expectTypeOf<recordWithLiteralUnionKeys>().toEqualTypeOf<Record<"Tuna" | "Salmon", string>>();
|
|
30
|
+
expectTypeOf<recordWithLiteralKey>().toEqualTypeOf<Record<"Tuna" | "Salmon" | 21, string>>();
|
|
31
|
+
expectTypeOf<recordWithLiteralUnionKeys>().toEqualTypeOf<Record<"Tuna" | "Salmon" | 21, string>>();
|
|
32
|
+
expectTypeOf<recordWithTypescriptEnum>().toEqualTypeOf<Record<Enum, string>>();
|
|
21
33
|
});
|
|
22
34
|
|
|
23
35
|
test("enum exhaustiveness", () => {
|
|
@@ -64,14 +76,76 @@ test("enum exhaustiveness", () => {
|
|
|
64
76
|
`);
|
|
65
77
|
});
|
|
66
78
|
|
|
79
|
+
test("typescript enum exhaustiveness", () => {
|
|
80
|
+
enum BigFish {
|
|
81
|
+
Tuna = 0,
|
|
82
|
+
Salmon = "Shark",
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const schema = z.record(z.enum(BigFish), z.string());
|
|
86
|
+
const value = {
|
|
87
|
+
[BigFish.Tuna]: "asdf",
|
|
88
|
+
[BigFish.Salmon]: "asdf",
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
expect(schema.parse(value)).toEqual(value);
|
|
92
|
+
|
|
93
|
+
expect(schema.safeParse({ [BigFish.Tuna]: "asdf", [BigFish.Salmon]: "asdf", Trout: "asdf" })).toMatchInlineSnapshot(`
|
|
94
|
+
{
|
|
95
|
+
"error": [ZodError: [
|
|
96
|
+
{
|
|
97
|
+
"code": "unrecognized_keys",
|
|
98
|
+
"keys": [
|
|
99
|
+
"Trout"
|
|
100
|
+
],
|
|
101
|
+
"path": [],
|
|
102
|
+
"message": "Unrecognized key: \\"Trout\\""
|
|
103
|
+
}
|
|
104
|
+
]],
|
|
105
|
+
"success": false,
|
|
106
|
+
}
|
|
107
|
+
`);
|
|
108
|
+
expect(schema.safeParse({ [BigFish.Tuna]: "asdf" })).toMatchInlineSnapshot(`
|
|
109
|
+
{
|
|
110
|
+
"error": [ZodError: [
|
|
111
|
+
{
|
|
112
|
+
"expected": "string",
|
|
113
|
+
"code": "invalid_type",
|
|
114
|
+
"path": [
|
|
115
|
+
"Shark"
|
|
116
|
+
],
|
|
117
|
+
"message": "Invalid input: expected string, received undefined"
|
|
118
|
+
}
|
|
119
|
+
]],
|
|
120
|
+
"success": false,
|
|
121
|
+
}
|
|
122
|
+
`);
|
|
123
|
+
expect(schema.safeParse({ [BigFish.Salmon]: "asdf" })).toMatchInlineSnapshot(`
|
|
124
|
+
{
|
|
125
|
+
"error": [ZodError: [
|
|
126
|
+
{
|
|
127
|
+
"expected": "string",
|
|
128
|
+
"code": "invalid_type",
|
|
129
|
+
"path": [
|
|
130
|
+
0
|
|
131
|
+
],
|
|
132
|
+
"message": "Invalid input: expected string, received undefined"
|
|
133
|
+
}
|
|
134
|
+
]],
|
|
135
|
+
"success": false,
|
|
136
|
+
}
|
|
137
|
+
`);
|
|
138
|
+
});
|
|
139
|
+
|
|
67
140
|
test("literal exhaustiveness", () => {
|
|
68
|
-
const schema = z.record(z.literal(["Tuna", "Salmon"]), z.string());
|
|
141
|
+
const schema = z.record(z.literal(["Tuna", "Salmon", 21]), z.string());
|
|
69
142
|
schema.parse({
|
|
70
143
|
Tuna: "asdf",
|
|
71
144
|
Salmon: "asdf",
|
|
145
|
+
21: "asdf",
|
|
72
146
|
});
|
|
73
147
|
|
|
74
|
-
expect(schema.safeParse({ Tuna: "asdf", Salmon: "asdf", Trout: "asdf" })).toMatchInlineSnapshot(`
|
|
148
|
+
expect(schema.safeParse({ Tuna: "asdf", Salmon: "asdf", 21: "asdf", Trout: "asdf" })).toMatchInlineSnapshot(`
|
|
75
149
|
{
|
|
76
150
|
"error": [ZodError: [
|
|
77
151
|
{
|
|
@@ -96,6 +170,14 @@ test("literal exhaustiveness", () => {
|
|
|
96
170
|
"Salmon"
|
|
97
171
|
],
|
|
98
172
|
"message": "Invalid input: expected string, received undefined"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"expected": "string",
|
|
176
|
+
"code": "invalid_type",
|
|
177
|
+
"path": [
|
|
178
|
+
21
|
|
179
|
+
],
|
|
180
|
+
"message": "Invalid input: expected string, received undefined"
|
|
99
181
|
}
|
|
100
182
|
]],
|
|
101
183
|
"success": false,
|
|
@@ -143,13 +225,14 @@ test("pipe exhaustiveness", () => {
|
|
|
143
225
|
});
|
|
144
226
|
|
|
145
227
|
test("union exhaustiveness", () => {
|
|
146
|
-
const schema = z.record(z.union([z.literal("Tuna"), z.literal("Salmon")]), z.string());
|
|
147
|
-
expect(schema.parse({ Tuna: "asdf", Salmon: "asdf" })).toEqual({
|
|
228
|
+
const schema = z.record(z.union([z.literal("Tuna"), z.literal("Salmon"), z.literal(21)]), z.string());
|
|
229
|
+
expect(schema.parse({ Tuna: "asdf", Salmon: "asdf", 21: "asdf" })).toEqual({
|
|
148
230
|
Tuna: "asdf",
|
|
149
231
|
Salmon: "asdf",
|
|
232
|
+
21: "asdf",
|
|
150
233
|
});
|
|
151
234
|
|
|
152
|
-
expect(schema.safeParse({ Tuna: "asdf", Salmon: "asdf", Trout: "asdf" })).toMatchInlineSnapshot(`
|
|
235
|
+
expect(schema.safeParse({ Tuna: "asdf", Salmon: "asdf", 21: "asdf", Trout: "asdf" })).toMatchInlineSnapshot(`
|
|
153
236
|
{
|
|
154
237
|
"error": [ZodError: [
|
|
155
238
|
{
|
|
@@ -174,6 +257,14 @@ test("union exhaustiveness", () => {
|
|
|
174
257
|
"Salmon"
|
|
175
258
|
],
|
|
176
259
|
"message": "Invalid input: expected string, received undefined"
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"expected": "string",
|
|
263
|
+
"code": "invalid_type",
|
|
264
|
+
"path": [
|
|
265
|
+
21
|
|
266
|
+
],
|
|
267
|
+
"message": "Invalid input: expected string, received undefined"
|
|
177
268
|
}
|
|
178
269
|
]],
|
|
179
270
|
"success": false,
|
package/src/v4/core/schemas.ts
CHANGED
|
@@ -2603,11 +2603,13 @@ export const $ZodRecord: core.$constructor<$ZodRecord> = /*@__PURE__*/ core.$con
|
|
|
2603
2603
|
|
|
2604
2604
|
const proms: Promise<any>[] = [];
|
|
2605
2605
|
|
|
2606
|
-
|
|
2607
|
-
|
|
2606
|
+
const values = def.keyType._zod.values;
|
|
2607
|
+
if (values) {
|
|
2608
2608
|
payload.value = {};
|
|
2609
|
+
const recordKeys = new Set<string | symbol>();
|
|
2609
2610
|
for (const key of values) {
|
|
2610
2611
|
if (typeof key === "string" || typeof key === "number" || typeof key === "symbol") {
|
|
2612
|
+
recordKeys.add(typeof key === "number" ? key.toString() : key);
|
|
2611
2613
|
const result = def.valueType._zod.run({ value: input[key], issues: [] }, ctx);
|
|
2612
2614
|
|
|
2613
2615
|
if (result instanceof Promise) {
|
|
@@ -2630,7 +2632,7 @@ export const $ZodRecord: core.$constructor<$ZodRecord> = /*@__PURE__*/ core.$con
|
|
|
2630
2632
|
|
|
2631
2633
|
let unrecognized!: string[];
|
|
2632
2634
|
for (const key in input) {
|
|
2633
|
-
if (!
|
|
2635
|
+
if (!recordKeys.has(key)) {
|
|
2634
2636
|
unrecognized = unrecognized ?? [];
|
|
2635
2637
|
unrecognized.push(key);
|
|
2636
2638
|
}
|
|
@@ -296,6 +296,29 @@ test("z.record", () => {
|
|
|
296
296
|
expect(() => z.parse(c, { a: "hello", b: "world" })).toThrow();
|
|
297
297
|
// extra keys
|
|
298
298
|
expect(() => z.parse(c, { a: "hello", b: "world", c: "world", d: "world" })).toThrow();
|
|
299
|
+
|
|
300
|
+
// literal union keys
|
|
301
|
+
const d = z.record(z.union([z.literal("a"), z.literal(0)]), z.string());
|
|
302
|
+
type d = z.output<typeof d>;
|
|
303
|
+
expectTypeOf<d>().toEqualTypeOf<Record<"a" | 0, string>>();
|
|
304
|
+
expect(z.parse(d, { a: "hello", 0: "world" })).toEqual({
|
|
305
|
+
a: "hello",
|
|
306
|
+
0: "world",
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// TypeScript enum keys
|
|
310
|
+
enum Enum {
|
|
311
|
+
A = 0,
|
|
312
|
+
B = "hi",
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const e = z.record(z.enum(Enum), z.string());
|
|
316
|
+
type e = z.output<typeof e>;
|
|
317
|
+
expectTypeOf<e>().toEqualTypeOf<Record<Enum, string>>();
|
|
318
|
+
expect(z.parse(e, { [Enum.A]: "hello", [Enum.B]: "world" })).toEqual({
|
|
319
|
+
[Enum.A]: "hello",
|
|
320
|
+
[Enum.B]: "world",
|
|
321
|
+
});
|
|
299
322
|
});
|
|
300
323
|
|
|
301
324
|
test("z.map", () => {
|
package/v4/core/schemas.cjs
CHANGED
|
@@ -1237,11 +1237,13 @@ exports.$ZodRecord = core.$constructor("$ZodRecord", (inst, def) => {
|
|
|
1237
1237
|
return payload;
|
|
1238
1238
|
}
|
|
1239
1239
|
const proms = [];
|
|
1240
|
-
|
|
1241
|
-
|
|
1240
|
+
const values = def.keyType._zod.values;
|
|
1241
|
+
if (values) {
|
|
1242
1242
|
payload.value = {};
|
|
1243
|
+
const recordKeys = new Set();
|
|
1243
1244
|
for (const key of values) {
|
|
1244
1245
|
if (typeof key === "string" || typeof key === "number" || typeof key === "symbol") {
|
|
1246
|
+
recordKeys.add(typeof key === "number" ? key.toString() : key);
|
|
1245
1247
|
const result = def.valueType._zod.run({ value: input[key], issues: [] }, ctx);
|
|
1246
1248
|
if (result instanceof Promise) {
|
|
1247
1249
|
proms.push(result.then((result) => {
|
|
@@ -1261,7 +1263,7 @@ exports.$ZodRecord = core.$constructor("$ZodRecord", (inst, def) => {
|
|
|
1261
1263
|
}
|
|
1262
1264
|
let unrecognized;
|
|
1263
1265
|
for (const key in input) {
|
|
1264
|
-
if (!
|
|
1266
|
+
if (!recordKeys.has(key)) {
|
|
1265
1267
|
unrecognized = unrecognized ?? [];
|
|
1266
1268
|
unrecognized.push(key);
|
|
1267
1269
|
}
|
package/v4/core/schemas.js
CHANGED
|
@@ -1206,11 +1206,13 @@ export const $ZodRecord = /*@__PURE__*/ core.$constructor("$ZodRecord", (inst, d
|
|
|
1206
1206
|
return payload;
|
|
1207
1207
|
}
|
|
1208
1208
|
const proms = [];
|
|
1209
|
-
|
|
1210
|
-
|
|
1209
|
+
const values = def.keyType._zod.values;
|
|
1210
|
+
if (values) {
|
|
1211
1211
|
payload.value = {};
|
|
1212
|
+
const recordKeys = new Set();
|
|
1212
1213
|
for (const key of values) {
|
|
1213
1214
|
if (typeof key === "string" || typeof key === "number" || typeof key === "symbol") {
|
|
1215
|
+
recordKeys.add(typeof key === "number" ? key.toString() : key);
|
|
1214
1216
|
const result = def.valueType._zod.run({ value: input[key], issues: [] }, ctx);
|
|
1215
1217
|
if (result instanceof Promise) {
|
|
1216
1218
|
proms.push(result.then((result) => {
|
|
@@ -1230,7 +1232,7 @@ export const $ZodRecord = /*@__PURE__*/ core.$constructor("$ZodRecord", (inst, d
|
|
|
1230
1232
|
}
|
|
1231
1233
|
let unrecognized;
|
|
1232
1234
|
for (const key in input) {
|
|
1233
|
-
if (!
|
|
1235
|
+
if (!recordKeys.has(key)) {
|
|
1234
1236
|
unrecognized = unrecognized ?? [];
|
|
1235
1237
|
unrecognized.push(key);
|
|
1236
1238
|
}
|