zod 3.26.0-canary.20250703T011142 → 3.26.0-canary.20250703T025502

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.
Files changed (269) hide show
  1. package/package.json +4 -4
  2. package/src/index.ts +4 -0
  3. package/src/v3/ZodError.ts +330 -0
  4. package/src/v3/benchmarks/datetime.ts +58 -0
  5. package/src/v3/benchmarks/discriminatedUnion.ts +80 -0
  6. package/src/v3/benchmarks/index.ts +59 -0
  7. package/src/v3/benchmarks/ipv4.ts +57 -0
  8. package/src/v3/benchmarks/object.ts +69 -0
  9. package/src/v3/benchmarks/primitives.ts +162 -0
  10. package/src/v3/benchmarks/realworld.ts +63 -0
  11. package/src/v3/benchmarks/string.ts +55 -0
  12. package/src/v3/benchmarks/union.ts +80 -0
  13. package/src/v3/errors.ts +13 -0
  14. package/src/v3/external.ts +6 -0
  15. package/src/v3/helpers/enumUtil.ts +17 -0
  16. package/src/v3/helpers/errorUtil.ts +8 -0
  17. package/src/v3/helpers/parseUtil.ts +176 -0
  18. package/src/v3/helpers/partialUtil.ts +34 -0
  19. package/src/v3/helpers/typeAliases.ts +2 -0
  20. package/src/v3/helpers/util.ts +224 -0
  21. package/src/v3/index.ts +4 -0
  22. package/src/v3/locales/en.ts +124 -0
  23. package/src/v3/standard-schema.ts +113 -0
  24. package/src/v3/tests/Mocker.ts +54 -0
  25. package/src/v3/tests/all-errors.test.ts +157 -0
  26. package/src/v3/tests/anyunknown.test.ts +28 -0
  27. package/src/v3/tests/array.test.ts +71 -0
  28. package/src/v3/tests/async-parsing.test.ts +388 -0
  29. package/src/v3/tests/async-refinements.test.ts +46 -0
  30. package/src/v3/tests/base.test.ts +29 -0
  31. package/src/v3/tests/bigint.test.ts +55 -0
  32. package/src/v3/tests/branded.test.ts +53 -0
  33. package/src/v3/tests/catch.test.ts +220 -0
  34. package/src/v3/tests/coerce.test.ts +133 -0
  35. package/src/v3/tests/complex.test.ts +56 -0
  36. package/src/v3/tests/custom.test.ts +31 -0
  37. package/src/v3/tests/date.test.ts +32 -0
  38. package/src/v3/tests/deepmasking.test.ts +186 -0
  39. package/src/v3/tests/default.test.ts +112 -0
  40. package/src/v3/tests/description.test.ts +33 -0
  41. package/src/v3/tests/discriminated-unions.test.ts +315 -0
  42. package/src/v3/tests/enum.test.ts +80 -0
  43. package/src/v3/tests/error.test.ts +551 -0
  44. package/src/v3/tests/firstparty.test.ts +87 -0
  45. package/src/v3/tests/firstpartyschematypes.test.ts +21 -0
  46. package/src/v3/tests/function.test.ts +257 -0
  47. package/src/v3/tests/generics.test.ts +48 -0
  48. package/src/v3/tests/instanceof.test.ts +37 -0
  49. package/src/v3/tests/intersection.test.ts +110 -0
  50. package/src/v3/tests/language-server.source.ts +76 -0
  51. package/src/v3/tests/language-server.test.ts +207 -0
  52. package/src/v3/tests/literal.test.ts +36 -0
  53. package/src/v3/tests/map.test.ts +110 -0
  54. package/src/v3/tests/masking.test.ts +4 -0
  55. package/src/v3/tests/mocker.test.ts +19 -0
  56. package/src/v3/tests/nan.test.ts +21 -0
  57. package/src/v3/tests/nativeEnum.test.ts +87 -0
  58. package/src/v3/tests/nullable.test.ts +42 -0
  59. package/src/v3/tests/number.test.ts +176 -0
  60. package/src/v3/tests/object-augmentation.test.ts +29 -0
  61. package/src/v3/tests/object-in-es5-env.test.ts +29 -0
  62. package/src/v3/tests/object.test.ts +434 -0
  63. package/src/v3/tests/optional.test.ts +42 -0
  64. package/src/v3/tests/parseUtil.test.ts +23 -0
  65. package/src/v3/tests/parser.test.ts +41 -0
  66. package/src/v3/tests/partials.test.ts +243 -0
  67. package/src/v3/tests/pickomit.test.ts +111 -0
  68. package/src/v3/tests/pipeline.test.ts +29 -0
  69. package/src/v3/tests/preprocess.test.ts +186 -0
  70. package/src/v3/tests/primitive.test.ts +440 -0
  71. package/src/v3/tests/promise.test.ts +90 -0
  72. package/src/v3/tests/readonly.test.ts +194 -0
  73. package/src/v3/tests/record.test.ts +171 -0
  74. package/src/v3/tests/recursive.test.ts +197 -0
  75. package/src/v3/tests/refine.test.ts +313 -0
  76. package/src/v3/tests/safeparse.test.ts +27 -0
  77. package/src/v3/tests/set.test.ts +142 -0
  78. package/src/v3/tests/standard-schema.test.ts +83 -0
  79. package/src/v3/tests/string.test.ts +916 -0
  80. package/src/v3/tests/transformer.test.ts +233 -0
  81. package/src/v3/tests/tuple.test.ts +90 -0
  82. package/src/v3/tests/unions.test.ts +57 -0
  83. package/src/v3/tests/validations.test.ts +133 -0
  84. package/src/v3/tests/void.test.ts +15 -0
  85. package/src/v3/types.ts +5136 -0
  86. package/src/v4/classic/checks.ts +30 -0
  87. package/src/v4/classic/coerce.ts +27 -0
  88. package/src/v4/classic/compat.ts +66 -0
  89. package/src/v4/classic/errors.ts +75 -0
  90. package/src/v4/classic/external.ts +50 -0
  91. package/src/v4/classic/index.ts +5 -0
  92. package/src/v4/classic/iso.ts +90 -0
  93. package/src/v4/classic/parse.ts +33 -0
  94. package/src/v4/classic/schemas.ts +2055 -0
  95. package/src/v4/classic/tests/anyunknown.test.ts +26 -0
  96. package/src/v4/classic/tests/array.test.ts +264 -0
  97. package/src/v4/classic/tests/assignability.test.ts +210 -0
  98. package/src/v4/classic/tests/async-parsing.test.ts +381 -0
  99. package/src/v4/classic/tests/async-refinements.test.ts +68 -0
  100. package/src/v4/classic/tests/base.test.ts +7 -0
  101. package/src/v4/classic/tests/bigint.test.ts +54 -0
  102. package/src/v4/classic/tests/brand.test.ts +65 -0
  103. package/src/v4/classic/tests/catch.test.ts +252 -0
  104. package/src/v4/classic/tests/coalesce.test.ts +20 -0
  105. package/src/v4/classic/tests/coerce.test.ts +160 -0
  106. package/src/v4/classic/tests/continuability.test.ts +352 -0
  107. package/src/v4/classic/tests/custom.test.ts +40 -0
  108. package/src/v4/classic/tests/date.test.ts +31 -0
  109. package/src/v4/classic/tests/datetime.test.ts +296 -0
  110. package/src/v4/classic/tests/default.test.ts +313 -0
  111. package/src/v4/classic/tests/description.test.ts +32 -0
  112. package/src/v4/classic/tests/discriminated-unions.test.ts +592 -0
  113. package/src/v4/classic/tests/enum.test.ts +285 -0
  114. package/src/v4/classic/tests/error-utils.test.ts +527 -0
  115. package/src/v4/classic/tests/error.test.ts +711 -0
  116. package/src/v4/classic/tests/file.test.ts +91 -0
  117. package/src/v4/classic/tests/firstparty.test.ts +175 -0
  118. package/src/v4/classic/tests/function.test.ts +268 -0
  119. package/src/v4/classic/tests/generics.test.ts +72 -0
  120. package/src/v4/classic/tests/index.test.ts +829 -0
  121. package/src/v4/classic/tests/instanceof.test.ts +34 -0
  122. package/src/v4/classic/tests/intersection.test.ts +171 -0
  123. package/src/v4/classic/tests/json.test.ts +108 -0
  124. package/src/v4/classic/tests/lazy.test.ts +227 -0
  125. package/src/v4/classic/tests/literal.test.ts +92 -0
  126. package/src/v4/classic/tests/map.test.ts +196 -0
  127. package/src/v4/classic/tests/nan.test.ts +21 -0
  128. package/src/v4/classic/tests/nested-refine.test.ts +168 -0
  129. package/src/v4/classic/tests/nonoptional.test.ts +86 -0
  130. package/src/v4/classic/tests/nullable.test.ts +22 -0
  131. package/src/v4/classic/tests/number.test.ts +247 -0
  132. package/src/v4/classic/tests/object.test.ts +553 -0
  133. package/src/v4/classic/tests/optional.test.ts +103 -0
  134. package/src/v4/classic/tests/partial.test.ts +147 -0
  135. package/src/v4/classic/tests/pickomit.test.ts +127 -0
  136. package/src/v4/classic/tests/pipe.test.ts +81 -0
  137. package/src/v4/classic/tests/prefault.test.ts +37 -0
  138. package/src/v4/classic/tests/preprocess.test.ts +298 -0
  139. package/src/v4/classic/tests/primitive.test.ts +175 -0
  140. package/src/v4/classic/tests/promise.test.ts +81 -0
  141. package/src/v4/classic/tests/prototypes.test.ts +23 -0
  142. package/src/v4/classic/tests/readonly.test.ts +252 -0
  143. package/src/v4/classic/tests/record.test.ts +332 -0
  144. package/src/v4/classic/tests/recursive-types.test.ts +325 -0
  145. package/src/v4/classic/tests/refine.test.ts +423 -0
  146. package/src/v4/classic/tests/registries.test.ts +195 -0
  147. package/src/v4/classic/tests/set.test.ts +179 -0
  148. package/src/v4/classic/tests/standard-schema.test.ts +57 -0
  149. package/src/v4/classic/tests/string-formats.test.ts +109 -0
  150. package/src/v4/classic/tests/string.test.ts +881 -0
  151. package/src/v4/classic/tests/stringbool.test.ts +66 -0
  152. package/src/v4/classic/tests/template-literal.test.ts +758 -0
  153. package/src/v4/classic/tests/to-json-schema.test.ts +2182 -0
  154. package/src/v4/classic/tests/transform.test.ts +250 -0
  155. package/src/v4/classic/tests/tuple.test.ts +163 -0
  156. package/src/v4/classic/tests/union.test.ts +94 -0
  157. package/src/v4/classic/tests/validations.test.ts +283 -0
  158. package/src/v4/classic/tests/void.test.ts +12 -0
  159. package/src/v4/core/api.ts +1592 -0
  160. package/src/v4/core/checks.ts +1285 -0
  161. package/src/v4/core/config.ts +15 -0
  162. package/src/v4/core/core.ts +134 -0
  163. package/src/v4/core/doc.ts +44 -0
  164. package/src/v4/core/errors.ts +420 -0
  165. package/src/v4/core/function.ts +176 -0
  166. package/src/v4/core/index.ts +15 -0
  167. package/src/v4/core/json-schema.ts +143 -0
  168. package/src/v4/core/parse.ts +94 -0
  169. package/src/v4/core/regexes.ts +135 -0
  170. package/src/v4/core/registries.ts +86 -0
  171. package/src/v4/core/schemas.ts +3781 -0
  172. package/src/v4/core/standard-schema.ts +64 -0
  173. package/src/v4/core/tests/index.test.ts +46 -0
  174. package/src/v4/core/tests/locales/be.test.ts +124 -0
  175. package/src/v4/core/tests/locales/en.test.ts +22 -0
  176. package/src/v4/core/tests/locales/ru.test.ts +128 -0
  177. package/src/v4/core/tests/locales/tr.test.ts +69 -0
  178. package/src/v4/core/to-json-schema.ts +944 -0
  179. package/src/v4/core/util.ts +775 -0
  180. package/src/v4/core/versions.ts +5 -0
  181. package/src/v4/core/zsf.ts +323 -0
  182. package/src/v4/index.ts +4 -0
  183. package/src/v4/locales/ar.ts +125 -0
  184. package/src/v4/locales/az.ts +121 -0
  185. package/src/v4/locales/be.ts +184 -0
  186. package/src/v4/locales/ca.ts +127 -0
  187. package/src/v4/locales/cs.ts +142 -0
  188. package/src/v4/locales/de.ts +124 -0
  189. package/src/v4/locales/en.ts +127 -0
  190. package/src/v4/locales/es.ts +125 -0
  191. package/src/v4/locales/fa.ts +134 -0
  192. package/src/v4/locales/fi.ts +131 -0
  193. package/src/v4/locales/fr-CA.ts +126 -0
  194. package/src/v4/locales/fr.ts +124 -0
  195. package/src/v4/locales/he.ts +125 -0
  196. package/src/v4/locales/hu.ts +126 -0
  197. package/src/v4/locales/id.ts +125 -0
  198. package/src/v4/locales/index.ts +38 -0
  199. package/src/v4/locales/it.ts +125 -0
  200. package/src/v4/locales/ja.ts +122 -0
  201. package/src/v4/locales/kh.ts +126 -0
  202. package/src/v4/locales/ko.ts +131 -0
  203. package/src/v4/locales/mk.ts +127 -0
  204. package/src/v4/locales/ms.ts +124 -0
  205. package/src/v4/locales/nl.ts +126 -0
  206. package/src/v4/locales/no.ts +124 -0
  207. package/src/v4/locales/ota.ts +125 -0
  208. package/src/v4/locales/pl.ts +126 -0
  209. package/src/v4/locales/ps.ts +133 -0
  210. package/src/v4/locales/pt.ts +123 -0
  211. package/src/v4/locales/ru.ts +184 -0
  212. package/src/v4/locales/sl.ts +126 -0
  213. package/src/v4/locales/sv.ts +127 -0
  214. package/src/v4/locales/ta.ts +125 -0
  215. package/src/v4/locales/th.ts +126 -0
  216. package/src/v4/locales/tr.ts +121 -0
  217. package/src/v4/locales/ua.ts +126 -0
  218. package/src/v4/locales/ur.ts +126 -0
  219. package/src/v4/locales/vi.ts +125 -0
  220. package/src/v4/locales/zh-CN.ts +123 -0
  221. package/src/v4/locales/zh-TW.ts +125 -0
  222. package/src/v4/mini/checks.ts +32 -0
  223. package/src/v4/mini/coerce.ts +22 -0
  224. package/src/v4/mini/external.ts +40 -0
  225. package/src/v4/mini/index.ts +3 -0
  226. package/src/v4/mini/iso.ts +62 -0
  227. package/src/v4/mini/parse.ts +1 -0
  228. package/src/v4/mini/schemas.ts +1579 -0
  229. package/src/v4/mini/tests/assignability.test.ts +129 -0
  230. package/src/v4/mini/tests/brand.test.ts +51 -0
  231. package/src/v4/mini/tests/checks.test.ts +144 -0
  232. package/src/v4/mini/tests/computed.test.ts +36 -0
  233. package/src/v4/mini/tests/error.test.ts +22 -0
  234. package/src/v4/mini/tests/functions.test.ts +43 -0
  235. package/src/v4/mini/tests/index.test.ts +871 -0
  236. package/src/v4/mini/tests/number.test.ts +95 -0
  237. package/src/v4/mini/tests/object.test.ts +185 -0
  238. package/src/v4/mini/tests/prototypes.test.ts +43 -0
  239. package/src/v4/mini/tests/recursive-types.test.ts +275 -0
  240. package/src/v4/mini/tests/string.test.ts +293 -0
  241. package/src/v4-mini/index.ts +1 -0
  242. package/v4/classic/compat.cjs +1 -7
  243. package/v4/classic/compat.d.cts +0 -2
  244. package/v4/classic/compat.d.ts +0 -2
  245. package/v4/classic/compat.js +0 -6
  246. package/v4/classic/external.cjs +2 -1
  247. package/v4/classic/external.d.cts +1 -1
  248. package/v4/classic/external.d.ts +1 -1
  249. package/v4/classic/external.js +1 -1
  250. package/v4/classic/schemas.d.cts +4 -4
  251. package/v4/classic/schemas.d.ts +4 -4
  252. package/v4/core/api.cjs +11 -10
  253. package/v4/core/api.js +11 -10
  254. package/v4/core/checks.cjs +6 -4
  255. package/v4/core/checks.js +6 -4
  256. package/v4/core/core.cjs +5 -1
  257. package/v4/core/core.d.cts +2 -0
  258. package/v4/core/core.d.ts +2 -0
  259. package/v4/core/core.js +4 -0
  260. package/v4/core/schemas.cjs +12 -13
  261. package/v4/core/schemas.js +12 -13
  262. package/v4/core/util.cjs +3 -0
  263. package/v4/core/util.d.cts +1 -1
  264. package/v4/core/util.d.ts +1 -1
  265. package/v4/core/util.js +3 -0
  266. package/v4/mini/external.cjs +2 -1
  267. package/v4/mini/external.d.cts +1 -1
  268. package/v4/mini/external.d.ts +1 -1
  269. package/v4/mini/external.js +1 -1
@@ -0,0 +1,592 @@
1
+ import { expect, expectTypeOf, test } from "vitest";
2
+
3
+ import * as z from "zod/v4";
4
+
5
+ test("_values", () => {
6
+ expect(z.string()._zod.values).toEqual(undefined);
7
+ expect(z.enum(["a", "b"])._zod.values).toEqual(new Set(["a", "b"]));
8
+ expect(z.nativeEnum({ a: "A", b: "B" })._zod.values).toEqual(new Set(["A", "B"]));
9
+ expect(z.literal("test")._zod.values).toEqual(new Set(["test"]));
10
+ expect(z.literal(123)._zod.values).toEqual(new Set([123]));
11
+ expect(z.literal(true)._zod.values).toEqual(new Set([true]));
12
+ expect(z.literal(BigInt(123))._zod.values).toEqual(new Set([BigInt(123)]));
13
+ expect(z.undefined()._zod.values).toEqual(new Set([undefined]));
14
+ expect(z.null()._zod.values).toEqual(new Set([null]));
15
+
16
+ const t = z.literal("test");
17
+ expect(t.optional()._zod.values).toEqual(new Set(["test", undefined]));
18
+ expect(t.nullable()._zod.values).toEqual(new Set(["test", null]));
19
+ expect(t.default("test")._zod.values).toEqual(new Set(["test"]));
20
+ expect(t.catch("test")._zod.values).toEqual(new Set(["test"]));
21
+
22
+ const pre = z.preprocess((val) => String(val), z.string()).pipe(z.literal("test"));
23
+ expect(pre._zod.values).toEqual(undefined);
24
+
25
+ const post = z.literal("test").transform((_) => Math.random());
26
+ expect(post._zod.values).toEqual(new Set(["test"]));
27
+ });
28
+
29
+ test("valid parse - object", () => {
30
+ expect(
31
+ z
32
+ .discriminatedUnion("type", [
33
+ z.object({ type: z.literal("a"), a: z.string() }),
34
+ z.object({ type: z.literal("b"), b: z.string() }),
35
+ ])
36
+ .parse({ type: "a", a: "abc" })
37
+ ).toEqual({ type: "a", a: "abc" });
38
+ });
39
+
40
+ test("valid - include discriminator key (deprecated)", () => {
41
+ expect(
42
+ z
43
+ .discriminatedUnion("type", [
44
+ z.object({ type: z.literal("a"), a: z.string() }),
45
+ z.object({ type: z.literal("b"), b: z.string() }),
46
+ ])
47
+ .parse({ type: "a", a: "abc" })
48
+ ).toEqual({ type: "a", a: "abc" });
49
+ });
50
+
51
+ test("valid - optional discriminator (object)", () => {
52
+ const schema = z.discriminatedUnion("type", [
53
+ z.object({ type: z.literal("a").optional(), a: z.string() }),
54
+ z.object({ type: z.literal("b"), b: z.string() }),
55
+ ]);
56
+ expect(schema.parse({ type: "a", a: "abc" })).toEqual({ type: "a", a: "abc" });
57
+ expect(schema.parse({ a: "abc" })).toEqual({ a: "abc" });
58
+ });
59
+
60
+ test("valid - discriminator value of various primitive types", () => {
61
+ const schema = z.discriminatedUnion("type", [
62
+ z.object({ type: z.literal("1"), val: z.string() }),
63
+ z.object({ type: z.literal(1), val: z.string() }),
64
+ z.object({ type: z.literal(BigInt(1)), val: z.string() }),
65
+ z.object({ type: z.literal("true"), val: z.string() }),
66
+ z.object({ type: z.literal(true), val: z.string() }),
67
+ z.object({ type: z.literal("null"), val: z.string() }),
68
+ z.object({ type: z.null(), val: z.string() }),
69
+ z.object({ type: z.literal("undefined"), val: z.string() }),
70
+ z.object({ type: z.undefined(), val: z.string() }),
71
+ ]);
72
+
73
+ expect(schema.parse({ type: "1", val: "val" })).toEqual({ type: "1", val: "val" });
74
+ expect(schema.parse({ type: 1, val: "val" })).toEqual({ type: 1, val: "val" });
75
+ expect(schema.parse({ type: BigInt(1), val: "val" })).toEqual({
76
+ type: BigInt(1),
77
+ val: "val",
78
+ });
79
+ expect(schema.parse({ type: "true", val: "val" })).toEqual({
80
+ type: "true",
81
+ val: "val",
82
+ });
83
+ expect(schema.parse({ type: true, val: "val" })).toEqual({
84
+ type: true,
85
+ val: "val",
86
+ });
87
+ expect(schema.parse({ type: "null", val: "val" })).toEqual({
88
+ type: "null",
89
+ val: "val",
90
+ });
91
+ expect(schema.parse({ type: null, val: "val" })).toEqual({
92
+ type: null,
93
+ val: "val",
94
+ });
95
+ expect(schema.parse({ type: "undefined", val: "val" })).toEqual({
96
+ type: "undefined",
97
+ val: "val",
98
+ });
99
+ expect(schema.parse({ type: undefined, val: "val" })).toEqual({
100
+ type: undefined,
101
+ val: "val",
102
+ });
103
+
104
+ const fail = schema.safeParse({
105
+ type: "not_a_key",
106
+ val: "val",
107
+ });
108
+ expect(fail.error).toBeInstanceOf(z.ZodError);
109
+ });
110
+
111
+ test("invalid - null", () => {
112
+ try {
113
+ z.discriminatedUnion("type", [
114
+ z.object({ type: z.literal("a"), a: z.string() }),
115
+ z.object({ type: z.literal("b"), b: z.string() }),
116
+ ]).parse(null);
117
+ throw new Error();
118
+ } catch (e: any) {
119
+ // [
120
+ // {
121
+ // code: z.ZodIssueCode.invalid_type,
122
+ // expected: z.ZodParsedType.object,
123
+ // input: null,
124
+ // message: "Expected object, received null",
125
+ // received: z.ZodParsedType.null,
126
+ // path: [],
127
+ // },
128
+ // ];
129
+ expect(e.issues).toMatchInlineSnapshot(`
130
+ [
131
+ {
132
+ "code": "invalid_type",
133
+ "expected": "object",
134
+ "message": "Invalid input: expected object, received null",
135
+ "path": [],
136
+ },
137
+ ]
138
+ `);
139
+ }
140
+ });
141
+
142
+ test("invalid discriminator value", () => {
143
+ const result = z
144
+ .discriminatedUnion("type", [
145
+ z.object({ type: z.literal("a"), a: z.string() }),
146
+ z.object({ type: z.literal("b"), b: z.string() }),
147
+ ])
148
+ .safeParse({ type: "x", a: "abc" });
149
+
150
+ expect(result).toMatchInlineSnapshot(`
151
+ {
152
+ "error": [ZodError: [
153
+ {
154
+ "code": "invalid_union",
155
+ "errors": [],
156
+ "note": "No matching discriminator",
157
+ "path": [
158
+ "type"
159
+ ],
160
+ "message": "Invalid input"
161
+ }
162
+ ]],
163
+ "success": false,
164
+ }
165
+ `);
166
+ });
167
+
168
+ test("invalid discriminator value - unionFallback", () => {
169
+ const result = z
170
+ .discriminatedUnion(
171
+ "type",
172
+ [z.object({ type: z.literal("a"), a: z.string() }), z.object({ type: z.literal("b"), b: z.string() })],
173
+ { unionFallback: true }
174
+ )
175
+ .safeParse({ type: "x", a: "abc" });
176
+ expect(result).toMatchInlineSnapshot(`
177
+ {
178
+ "error": [ZodError: [
179
+ {
180
+ "code": "invalid_union",
181
+ "errors": [
182
+ [
183
+ {
184
+ "code": "invalid_value",
185
+ "values": [
186
+ "a"
187
+ ],
188
+ "path": [
189
+ "type"
190
+ ],
191
+ "message": "Invalid input: expected \\"a\\""
192
+ }
193
+ ],
194
+ [
195
+ {
196
+ "code": "invalid_value",
197
+ "values": [
198
+ "b"
199
+ ],
200
+ "path": [
201
+ "type"
202
+ ],
203
+ "message": "Invalid input: expected \\"b\\""
204
+ },
205
+ {
206
+ "expected": "string",
207
+ "code": "invalid_type",
208
+ "path": [
209
+ "b"
210
+ ],
211
+ "message": "Invalid input: expected string, received undefined"
212
+ }
213
+ ]
214
+ ],
215
+ "path": [],
216
+ "message": "Invalid input"
217
+ }
218
+ ]],
219
+ "success": false,
220
+ }
221
+ `);
222
+ });
223
+
224
+ test("valid discriminator value, invalid data", () => {
225
+ const result = z
226
+ .discriminatedUnion("type", [
227
+ z.object({ type: z.literal("a"), a: z.string() }),
228
+ z.object({ type: z.literal("b"), b: z.string() }),
229
+ ])
230
+ .safeParse({ type: "a", b: "abc" });
231
+
232
+ // [
233
+ // {
234
+ // code: z.ZodIssueCode.invalid_type,
235
+ // expected: z.ZodParsedType.string,
236
+ // message: "Required",
237
+ // path: ["a"],
238
+ // received: z.ZodParsedType.undefined,
239
+ // },
240
+ // ];
241
+ expect(result).toMatchInlineSnapshot(`
242
+ {
243
+ "error": [ZodError: [
244
+ {
245
+ "expected": "string",
246
+ "code": "invalid_type",
247
+ "path": [
248
+ "a"
249
+ ],
250
+ "message": "Invalid input: expected string, received undefined"
251
+ }
252
+ ]],
253
+ "success": false,
254
+ }
255
+ `);
256
+ });
257
+
258
+ test("wrong schema - missing discriminator", () => {
259
+ try {
260
+ z.discriminatedUnion("type", [
261
+ z.object({ type: z.literal("a"), a: z.string() }),
262
+ z.object({ b: z.string() }) as any,
263
+ ])._zod.propValues;
264
+ throw new Error();
265
+ } catch (e: any) {
266
+ expect(e.message.includes("Invalid discriminated union option")).toBe(true);
267
+ }
268
+ });
269
+
270
+ // removed to account for unions of unions
271
+ // test("wrong schema - duplicate discriminator values", () => {
272
+ // try {
273
+ // z.discriminatedUnion("type",[
274
+ // z.object({ type: z.literal("a"), a: z.string() }),
275
+ // z.object({ type: z.literal("a"), b: z.string() }),
276
+ // ]);
277
+ // throw new Error();
278
+ // } catch (e: any) {
279
+ // expect(e.message.includes("Duplicate discriminator value")).toEqual(true);
280
+ // }
281
+ // });
282
+
283
+ test("async - valid", async () => {
284
+ const schema = await z.discriminatedUnion("type", [
285
+ z.object({
286
+ type: z.literal("a"),
287
+ a: z
288
+ .string()
289
+ .refine(async () => true)
290
+ .transform(async (val) => Number(val)),
291
+ }),
292
+ z.object({
293
+ type: z.literal("b"),
294
+ b: z.string(),
295
+ }),
296
+ ]);
297
+ const data = { type: "a", a: "1" };
298
+ const result = await schema.safeParseAsync(data);
299
+ expect(result.data).toEqual({ type: "a", a: 1 });
300
+ });
301
+
302
+ test("async - invalid", async () => {
303
+ // try {
304
+ const a = z.discriminatedUnion("type", [
305
+ z.object({
306
+ type: z.literal("a"),
307
+ a: z
308
+ .string()
309
+ .refine(async () => true)
310
+ .transform(async (val) => val),
311
+ }),
312
+ z.object({
313
+ type: z.literal("b"),
314
+ b: z.string(),
315
+ }),
316
+ ]);
317
+ const result = await a.safeParseAsync({ type: "a", a: 1 });
318
+
319
+ // expect(JSON.parse(e.message)).toEqual([
320
+ // {
321
+ // code: "invalid_type",
322
+ // expected: "string",
323
+ // input: 1,
324
+ // received: "number",
325
+ // path: ["a"],
326
+ // message: "Expected string, received number",
327
+ // },
328
+ // ]);
329
+ expect(result.error).toMatchInlineSnapshot(`
330
+ [ZodError: [
331
+ {
332
+ "expected": "string",
333
+ "code": "invalid_type",
334
+ "path": [
335
+ "a"
336
+ ],
337
+ "message": "Invalid input: expected string, received number"
338
+ }
339
+ ]]
340
+ `);
341
+ });
342
+
343
+ test("valid - literals with .default or .pipe", () => {
344
+ const schema = z.discriminatedUnion("type", [
345
+ z.object({
346
+ type: z.literal("foo").default("foo"),
347
+ a: z.string(),
348
+ }),
349
+ z.object({
350
+ type: z.literal("custom"),
351
+ method: z.string(),
352
+ }),
353
+ z.object({
354
+ type: z.literal("bar").transform((val) => val),
355
+ c: z.string(),
356
+ }),
357
+ ]);
358
+ expect(schema.parse({ type: "foo", a: "foo" })).toEqual({
359
+ type: "foo",
360
+ a: "foo",
361
+ });
362
+ });
363
+
364
+ test("enum and nativeEnum", () => {
365
+ enum MyEnum {
366
+ d = 0,
367
+ e = "e",
368
+ }
369
+
370
+ const schema = z.discriminatedUnion("key", [
371
+ z.object({
372
+ key: z.literal("a"),
373
+ // Add other properties specific to this option
374
+ }),
375
+ z.object({
376
+ key: z.enum(["b", "c"]),
377
+ // Add other properties specific to this option
378
+ }),
379
+ z.object({
380
+ key: z.nativeEnum(MyEnum),
381
+ // Add other properties specific to this option
382
+ }),
383
+ ]);
384
+
385
+ type schema = z.infer<typeof schema>;
386
+ expectTypeOf<schema>().toEqualTypeOf<{ key: "a" } | { key: "b" | "c" } | { key: MyEnum.d | MyEnum.e }>();
387
+
388
+ schema.parse({ key: "a" });
389
+ schema.parse({ key: "b" });
390
+ schema.parse({ key: "c" });
391
+ schema.parse({ key: MyEnum.d });
392
+ schema.parse({ key: MyEnum.e });
393
+ schema.parse({ key: "e" });
394
+ });
395
+
396
+ test("branded", () => {
397
+ const schema = z.discriminatedUnion("key", [
398
+ z.object({
399
+ key: z.literal("a"),
400
+ // Add other properties specific to this option
401
+ }),
402
+ z.object({
403
+ key: z.literal("b").brand<"asdfasdf">(),
404
+ // Add other properties specific to this option
405
+ }),
406
+ ]);
407
+
408
+ type schema = z.infer<typeof schema>;
409
+ expectTypeOf<schema>().toEqualTypeOf<{ key: "a" } | { key: "b" & z.core.$brand<"asdfasdf"> }>();
410
+
411
+ schema.parse({ key: "a" });
412
+ schema.parse({ key: "b" });
413
+ expect(() => {
414
+ schema.parse({ key: "c" });
415
+ }).toThrow();
416
+ });
417
+
418
+ test("optional and nullable", () => {
419
+ const schema = z.discriminatedUnion("key", [
420
+ z.object({
421
+ key: z.literal("a").optional(),
422
+ a: z.literal(true),
423
+ }),
424
+ z.object({
425
+ key: z.literal("b").nullable(),
426
+ b: z.literal(true),
427
+ // Add other properties specific to this option
428
+ }),
429
+ ]);
430
+
431
+ type schema = z.infer<typeof schema>;
432
+ expectTypeOf<schema>().toEqualTypeOf<{ key?: "a" | undefined; a: true } | { key: "b" | null; b: true }>();
433
+
434
+ schema.parse({ key: "a", a: true });
435
+ schema.parse({ key: undefined, a: true });
436
+ schema.parse({ key: "b", b: true });
437
+ schema.parse({ key: null, b: true });
438
+ expect(() => {
439
+ schema.parse({ key: null, a: true });
440
+ }).toThrow();
441
+ expect(() => {
442
+ schema.parse({ key: "b", a: true });
443
+ }).toThrow();
444
+
445
+ const value = schema.parse({ key: null, b: true });
446
+
447
+ if (!("key" in value)) value.a;
448
+ if (value.key === undefined) value.a;
449
+ if (value.key === "a") value.a;
450
+ if (value.key === "b") value.b;
451
+ if (value.key === null) value.b;
452
+ });
453
+
454
+ test("multiple discriminators", () => {
455
+ const FreeConfig = z.object({
456
+ type: z.literal("free"),
457
+ min_cents: z.null(),
458
+ });
459
+
460
+ // console.log(FreeConfig.shape.type);
461
+ const PricedConfig = z.object({
462
+ type: z.literal("fiat-price"),
463
+ // min_cents: z.int().nullable(),
464
+ min_cents: z.null(),
465
+ });
466
+
467
+ const Config = z.discriminatedUnion("type", [FreeConfig, PricedConfig]);
468
+
469
+ Config.parse({
470
+ min_cents: null,
471
+ type: "fiat-price",
472
+ name: "Standard",
473
+ });
474
+
475
+ expect(() => {
476
+ Config.parse({
477
+ min_cents: null,
478
+ type: "not real",
479
+ name: "Standard",
480
+ });
481
+ }).toThrow();
482
+ });
483
+
484
+ test("single element union", () => {
485
+ const schema = z.object({
486
+ a: z.literal("discKey"),
487
+ b: z.enum(["apple", "banana"]),
488
+ c: z.object({ id: z.string() }),
489
+ });
490
+
491
+ const input = {
492
+ a: "discKey",
493
+ b: "apple",
494
+ c: {}, // Invalid, as schema requires `id` property
495
+ };
496
+
497
+ // Validation must fail here, but it doesn't
498
+
499
+ const u = z.discriminatedUnion("a", [schema]);
500
+ const result = u.safeParse(input);
501
+ expect(result).toMatchObject({ success: false });
502
+ expect(result).toMatchInlineSnapshot(`
503
+ {
504
+ "error": [ZodError: [
505
+ {
506
+ "expected": "string",
507
+ "code": "invalid_type",
508
+ "path": [
509
+ "c",
510
+ "id"
511
+ ],
512
+ "message": "Invalid input: expected string, received undefined"
513
+ }
514
+ ]],
515
+ "success": false,
516
+ }
517
+ `);
518
+
519
+ expect(u.options.length).toEqual(1);
520
+ });
521
+
522
+ test("nested discriminated unions", () => {
523
+ const BaseError = z.object({ status: z.literal("failed"), message: z.string() });
524
+ const MyErrors = z.discriminatedUnion("code", [
525
+ BaseError.extend({ code: z.literal(400) }),
526
+ BaseError.extend({ code: z.literal(401) }),
527
+ BaseError.extend({ code: z.literal(500) }),
528
+ ]);
529
+
530
+ const MyResult = z.discriminatedUnion("status", [
531
+ z.object({ status: z.literal("success"), data: z.string() }),
532
+ MyErrors,
533
+ ]);
534
+
535
+ expect(MyErrors._zod.propValues).toMatchInlineSnapshot(`
536
+ {
537
+ "code": Set {
538
+ 400,
539
+ 401,
540
+ 500,
541
+ },
542
+ "status": Set {
543
+ "failed",
544
+ },
545
+ }
546
+ `);
547
+ expect(MyResult._zod.propValues).toMatchInlineSnapshot(`
548
+ {
549
+ "code": Set {
550
+ 400,
551
+ 401,
552
+ 500,
553
+ },
554
+ "status": Set {
555
+ "success",
556
+ "failed",
557
+ },
558
+ }
559
+ `);
560
+
561
+ const result = MyResult.parse({ status: "success", data: "hello" });
562
+ expect(result).toMatchInlineSnapshot(`
563
+ {
564
+ "data": "hello",
565
+ "status": "success",
566
+ }
567
+ `);
568
+ const result2 = MyResult.parse({ status: "failed", code: 400, message: "bad request" });
569
+ expect(result2).toMatchInlineSnapshot(`
570
+ {
571
+ "code": 400,
572
+ "message": "bad request",
573
+ "status": "failed",
574
+ }
575
+ `);
576
+ const result3 = MyResult.parse({ status: "failed", code: 401, message: "unauthorized" });
577
+ expect(result3).toMatchInlineSnapshot(`
578
+ {
579
+ "code": 401,
580
+ "message": "unauthorized",
581
+ "status": "failed",
582
+ }
583
+ `);
584
+ const result4 = MyResult.parse({ status: "failed", code: 500, message: "internal server error" });
585
+ expect(result4).toMatchInlineSnapshot(`
586
+ {
587
+ "code": 500,
588
+ "message": "internal server error",
589
+ "status": "failed",
590
+ }
591
+ `);
592
+ });