Buckets:
| import { expect, expectTypeOf, test } from "vitest"; | |
| import * as z from "zod/v4"; | |
| import * as core from "zod/v4/core"; | |
| const Test = z.object({ | |
| f1: z.number(), | |
| f2: z.string().optional(), | |
| f3: z.string().nullable(), | |
| f4: z.array(z.object({ t: z.union([z.string(), z.boolean()]) })), | |
| }); | |
| test("object type inference", () => { | |
| type TestType = { | |
| f1: number; | |
| f2?: string | undefined; | |
| f3: string | null; | |
| f4: { t: string | boolean }[]; | |
| }; | |
| expectTypeOf<z.TypeOf<typeof Test>>().toEqualTypeOf<TestType>(); | |
| }); | |
| test("unknown throw", () => { | |
| const asdf: unknown = 35; | |
| expect(() => Test.parse(asdf)).toThrow(); | |
| }); | |
| test("shape() should return schema of particular key", () => { | |
| const f1Schema = Test.shape.f1; | |
| const f2Schema = Test.shape.f2; | |
| const f3Schema = Test.shape.f3; | |
| const f4Schema = Test.shape.f4; | |
| expect(f1Schema).toBeInstanceOf(z.ZodNumber); | |
| expect(f2Schema).toBeInstanceOf(z.ZodOptional); | |
| expect(f3Schema).toBeInstanceOf(z.ZodNullable); | |
| expect(f4Schema).toBeInstanceOf(z.ZodArray); | |
| }); | |
| test("correct parsing", () => { | |
| Test.parse({ | |
| f1: 12, | |
| f2: "string", | |
| f3: "string", | |
| f4: [ | |
| { | |
| t: "string", | |
| }, | |
| ], | |
| }); | |
| Test.parse({ | |
| f1: 12, | |
| f3: null, | |
| f4: [ | |
| { | |
| t: false, | |
| }, | |
| ], | |
| }); | |
| }); | |
| test("nonstrict by default", () => { | |
| z.object({ points: z.number() }).parse({ | |
| points: 2314, | |
| unknown: "asdf", | |
| }); | |
| }); | |
| test("parse optional keys ", () => { | |
| const schema = z.object({ | |
| a: z.string().optional(), | |
| }); | |
| expect(schema.parse({ a: "asdf" })).toEqual({ a: "asdf" }); | |
| }); | |
| test("empty object", () => { | |
| const schema = z.object({}); | |
| expect(schema.parse({})).toEqual({}); | |
| expect(schema.parse({ name: "asdf" })).toEqual({}); | |
| expect(schema.safeParse(null).success).toEqual(false); | |
| expect(schema.safeParse("asdf").success).toEqual(false); | |
| expectTypeOf<z.output<typeof schema>>().toEqualTypeOf<Record<string, never>>(); | |
| }); | |
| const data = { | |
| points: 2314, | |
| unknown: "asdf", | |
| }; | |
| test("strip by default", () => { | |
| const val = z.object({ points: z.number() }).parse(data); | |
| expect(val).toEqual({ points: 2314 }); | |
| }); | |
| test("unknownkeys override", () => { | |
| const val = z.object({ points: z.number() }).strict().passthrough().strip().passthrough().parse(data); | |
| expect(val).toEqual(data); | |
| }); | |
| test("passthrough unknown", () => { | |
| const val = z.object({ points: z.number() }).passthrough().parse(data); | |
| expect(val).toEqual(data); | |
| }); | |
| test("strip unknown", () => { | |
| const val = z.object({ points: z.number() }).strip().parse(data); | |
| expect(val).toEqual({ points: 2314 }); | |
| }); | |
| test("strict", () => { | |
| const val = z.object({ points: z.number() }).strict().safeParse(data); | |
| expect(val.success).toEqual(false); | |
| }); | |
| test("catchall inference", () => { | |
| const o1 = z | |
| .object({ | |
| first: z.string(), | |
| }) | |
| .catchall(z.number()); | |
| const d1 = o1.parse({ first: "asdf", num: 1243 }); | |
| // expectTypeOf<(typeof d1)["asdf"]>().toEqualTypeOf<number>(); | |
| expectTypeOf<(typeof d1)["first"]>().toEqualTypeOf<string>(); | |
| }); | |
| test("catchall overrides strict", () => { | |
| const o1 = z.object({ first: z.string().optional() }).strict().catchall(z.number()); | |
| // should run fine | |
| // setting a catchall overrides the unknownKeys behavior | |
| o1.parse({ | |
| asdf: 1234, | |
| }); | |
| // should only run catchall validation | |
| // against unknown keys | |
| o1.parse({ | |
| first: "asdf", | |
| asdf: 1234, | |
| }); | |
| }); | |
| test("catchall overrides strict", () => { | |
| const o1 = z | |
| .object({ | |
| first: z.string(), | |
| }) | |
| .strict() | |
| .catchall(z.number()); | |
| // should run fine | |
| // setting a catchall overrides the unknownKeys behavior | |
| o1.parse({ | |
| first: "asdf", | |
| asdf: 1234, | |
| }); | |
| }); | |
| test("optional keys are unset", () => { | |
| const SNamedEntity = z.object({ | |
| id: z.string(), | |
| set: z.string().optional(), | |
| unset: z.string().optional(), | |
| }); | |
| const result = SNamedEntity.parse({ | |
| id: "asdf", | |
| set: undefined, | |
| }); | |
| expect(Object.keys(result)).toEqual(["id", "set"]); | |
| }); | |
| test("catchall parsing", async () => { | |
| const result = z.object({ name: z.string() }).catchall(z.number()).parse({ name: "Foo", validExtraKey: 61 }); | |
| expect(result).toEqual({ name: "Foo", validExtraKey: 61 }); | |
| const result2 = z | |
| .object({ name: z.string() }) | |
| .catchall(z.number()) | |
| .safeParse({ name: "Foo", validExtraKey: 61, invalid: "asdf" }); | |
| expect(result2.success).toEqual(false); | |
| }); | |
| test("nonexistent keys", async () => { | |
| const Schema = z.union([z.object({ a: z.string() }), z.object({ b: z.number() })]); | |
| const obj = { a: "A" }; | |
| const result = await Schema.spa(obj); // Works with 1.11.10, breaks with 2.0.0-beta.21 | |
| expect(result.success).toBe(true); | |
| }); | |
| test("test async union", async () => { | |
| const Schema2 = z.union([ | |
| z.object({ | |
| ty: z.string(), | |
| }), | |
| z.object({ | |
| ty: z.number(), | |
| }), | |
| ]); | |
| const obj = { ty: "A" }; | |
| const result = await Schema2.spa(obj); // Works with 1.11.10, breaks with 2.0.0-beta.21 | |
| expect(result.success).toEqual(true); | |
| }); | |
| test("test inferred merged type", async () => { | |
| const asdf = z.object({ a: z.string() }).merge(z.object({ a: z.number() })); | |
| type asdf = z.infer<typeof asdf>; | |
| expectTypeOf<asdf>().toEqualTypeOf<{ a: number }>(); | |
| }); | |
| test("inferred type with Record shape", () => { | |
| type A = z.ZodObject<Record<string, z.ZodType<string, number>>>; | |
| expectTypeOf<z.infer<A>>().toEqualTypeOf<Record<string, string>>(); | |
| expectTypeOf<z.input<A>>().toEqualTypeOf<Record<string, number>>(); | |
| type B = z.ZodObject; | |
| expectTypeOf<z.infer<B>>().toEqualTypeOf<Record<string, unknown>>(); | |
| expectTypeOf<z.input<B>>().toEqualTypeOf<Record<string, unknown>>(); | |
| }); | |
| test("inferred merged object type with optional properties", async () => { | |
| const Merged = z | |
| .object({ a: z.string(), b: z.string().optional() }) | |
| .merge(z.object({ a: z.string().optional(), b: z.string() })); | |
| type Merged = z.infer<typeof Merged>; | |
| expectTypeOf<Merged>().toEqualTypeOf<{ a?: string; b: string }>(); | |
| expectTypeOf<Merged>().toEqualTypeOf<{ a?: string; b: string }>(); | |
| }); | |
| test("inferred unioned object type with optional properties", async () => { | |
| const Unioned = z.union([ | |
| z.object({ a: z.string(), b: z.string().optional() }), | |
| z.object({ a: z.string().optional(), b: z.string() }), | |
| ]); | |
| type Unioned = z.infer<typeof Unioned>; | |
| expectTypeOf<Unioned>().toEqualTypeOf<{ a: string; b?: string } | { a?: string; b: string }>(); | |
| }); | |
| test("inferred enum type", async () => { | |
| const Enum = z.object({ a: z.string(), b: z.string().optional() }).keyof(); | |
| expect(Enum.enum).toEqual({ | |
| a: "a", | |
| b: "b", | |
| }); | |
| expect(Enum._zod.def.entries).toEqual({ | |
| a: "a", | |
| b: "b", | |
| }); | |
| type Enum = z.infer<typeof Enum>; | |
| expectTypeOf<Enum>().toEqualTypeOf<"a" | "b">(); | |
| }); | |
| test("z.keyof returns enum", () => { | |
| const User = z.object({ name: z.string(), age: z.number() }); | |
| const keysSchema = z.keyof(User); | |
| expect(keysSchema.enum).toEqual({ | |
| name: "name", | |
| age: "age", | |
| }); | |
| expect(keysSchema._zod.def.entries).toEqual({ | |
| name: "name", | |
| age: "age", | |
| }); | |
| type Keys = z.infer<typeof keysSchema>; | |
| expectTypeOf<Keys>().toEqualTypeOf<"name" | "age">(); | |
| }); | |
| test("inferred partial object type with optional properties", async () => { | |
| const Partial = z.object({ a: z.string(), b: z.string().optional() }).partial(); | |
| type Partial = z.infer<typeof Partial>; | |
| expectTypeOf<Partial>().toEqualTypeOf<{ a?: string; b?: string }>(); | |
| }); | |
| test("inferred picked object type with optional properties", async () => { | |
| const Picked = z.object({ a: z.string(), b: z.string().optional() }).pick({ b: true }); | |
| type Picked = z.infer<typeof Picked>; | |
| expectTypeOf<Picked>().toEqualTypeOf<{ b?: string }>(); | |
| }); | |
| test("inferred type for unknown/any keys", () => { | |
| const myType = z.object({ | |
| anyOptional: z.any().optional(), | |
| anyRequired: z.any(), | |
| unknownOptional: z.unknown().optional(), | |
| unknownRequired: z.unknown(), | |
| }); | |
| type myType = z.infer<typeof myType>; | |
| expectTypeOf<myType>().toEqualTypeOf<{ | |
| anyOptional?: any; | |
| anyRequired: any; | |
| unknownOptional?: unknown; | |
| unknownRequired: unknown; | |
| }>(); | |
| }); | |
| test("strictObject", async () => { | |
| const strictObj = z.strictObject({ | |
| name: z.string(), | |
| }); | |
| const syncResult = strictObj.safeParse({ name: "asdf", unexpected: 13 }); | |
| expect(syncResult.success).toEqual(false); | |
| const asyncResult = await strictObj.spa({ name: "asdf", unexpected: 13 }); | |
| expect(asyncResult.success).toEqual(false); | |
| }); | |
| test("object with refine", async () => { | |
| const schema = z | |
| .object({ | |
| a: z.string().default("foo"), | |
| b: z.number(), | |
| }) | |
| .refine(() => true); | |
| expect(schema.parse({ b: 5 })).toEqual({ b: 5, a: "foo" }); | |
| const result = await schema.parseAsync({ b: 5 }); | |
| expect(result).toEqual({ b: 5, a: "foo" }); | |
| }); | |
| test("intersection of object with date", async () => { | |
| const schema = z.object({ | |
| a: z.date(), | |
| }); | |
| expect(z.intersection(schema, schema).parse({ a: new Date(1637353595983) })).toEqual({ | |
| a: new Date(1637353595983), | |
| }); | |
| const result = await schema.parseAsync({ a: new Date(1637353595983) }); | |
| expect(result).toEqual({ a: new Date(1637353595983) }); | |
| }); | |
| test("intersection of object with refine with date", async () => { | |
| const schema = z | |
| .object({ | |
| a: z.date(), | |
| }) | |
| .refine(() => true); | |
| expect(z.intersection(schema, schema).parse({ a: new Date(1637353595983) })).toEqual({ | |
| a: new Date(1637353595983), | |
| }); | |
| const result = await schema.parseAsync({ a: new Date(1637353595983) }); | |
| expect(result).toEqual({ a: new Date(1637353595983) }); | |
| }); | |
| test("constructor key", () => { | |
| const person = z | |
| .object({ | |
| name: z.string(), | |
| }) | |
| .strict(); | |
| expect(() => | |
| person.parse({ | |
| name: "bob dylan", | |
| constructor: 61, | |
| }) | |
| ).toThrow(); | |
| }); | |
| test("constructor key", () => { | |
| const Example = z.object({ | |
| prop: z.string(), | |
| opt: z.number().optional(), | |
| arr: z.string().array(), | |
| }); | |
| type Example = z.infer<typeof Example>; | |
| expectTypeOf<keyof Example>().toEqualTypeOf<"prop" | "opt" | "arr">(); | |
| }); | |
| test("catchall", () => { | |
| const a = z.object({}); | |
| expect(a._zod.def.catchall).toBeUndefined(); | |
| const b = z.strictObject({}); | |
| expect(b._zod.def.catchall).toBeInstanceOf(core.$ZodNever); | |
| const c = z.looseObject({}); | |
| expect(c._zod.def.catchall).toBeInstanceOf(core.$ZodUnknown); | |
| const d = z.object({}).catchall(z.number()); | |
| expect(d._zod.def.catchall).toBeInstanceOf(core.$ZodNumber); | |
| }); | |
| test("unknownkeys merging", () => { | |
| // This one is "strict" | |
| const a = z.looseObject({ | |
| a: z.string(), | |
| }); | |
| const b = z.strictObject({ b: z.string() }); | |
| // incoming object overrides | |
| const c = a.merge(b); | |
| expect(c._zod.def.catchall).toBeInstanceOf(core.$ZodNever); | |
| }); | |
| const personToExtend = z.object({ | |
| firstName: z.string(), | |
| lastName: z.string(), | |
| }); | |
| test("extend() should return schema with new key", () => { | |
| const PersonWithNickname = personToExtend.extend({ nickName: z.string() }); | |
| type PersonWithNickname = z.infer<typeof PersonWithNickname>; | |
| const expected = { firstName: "f", nickName: "n", lastName: "l" }; | |
| const actual = PersonWithNickname.parse(expected); | |
| expect(actual).toEqual(expected); | |
| expectTypeOf<keyof PersonWithNickname>().toEqualTypeOf<"firstName" | "lastName" | "nickName">(); | |
| expectTypeOf<PersonWithNickname>().toEqualTypeOf<{ firstName: string; lastName: string; nickName: string }>(); | |
| }); | |
| test("extend() should have power to override existing key", () => { | |
| const PersonWithNumberAsLastName = personToExtend.extend({ | |
| lastName: z.number(), | |
| }); | |
| type PersonWithNumberAsLastName = z.infer<typeof PersonWithNumberAsLastName>; | |
| const expected = { firstName: "f", lastName: 42 }; | |
| const actual = PersonWithNumberAsLastName.parse(expected); | |
| expect(actual).toEqual(expected); | |
| expectTypeOf<PersonWithNumberAsLastName>().toEqualTypeOf<{ firstName: string; lastName: number }>(); | |
| }); | |
| test("safeExtend() maintains refinements", () => { | |
| const schema = z.object({ name: z.string().min(1) }); | |
| const extended = schema.safeExtend({ name: z.string().min(2) }); | |
| expect(() => extended.parse({ name: "" })).toThrow(); | |
| expect(extended.parse({ name: "ab" })).toEqual({ name: "ab" }); | |
| type Extended = z.infer<typeof extended>; | |
| expectTypeOf<Extended>().toEqualTypeOf<{ name: string }>(); | |
| // @ts-expect-error | |
| schema.safeExtend({ name: z.number() }); | |
| }); | |
| test("passthrough index signature", () => { | |
| const a = z.object({ a: z.string() }); | |
| type a = z.infer<typeof a>; | |
| expectTypeOf<a>().toEqualTypeOf<{ a: string }>(); | |
| const b = a.passthrough(); | |
| type b = z.infer<typeof b>; | |
| expectTypeOf<b>().toEqualTypeOf<{ a: string; [k: string]: unknown }>(); | |
| }); | |
| // test("xor", () => { | |
| // type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never }; | |
| // type XOR<T, U> = T extends object ? (U extends object ? (Without<T, U> & U) | (Without<U, T> & T) : U) : T; | |
| // type A = { name: string; a: number }; | |
| // type B = { name: string; b: number }; | |
| // type C = XOR<A, B>; | |
| // type Outer = { data: C }; | |
| // const Outer = z.object({ | |
| // data: z.union([z.object({ name: z.string(), a: z.number() }), z.object({ name: z.string(), b: z.number() })]), | |
| // }) satisfies z.ZodType<Outer, any>; | |
| // }); | |
| test("assignability", () => { | |
| z.object({ a: z.string() }) satisfies z.ZodObject<{ a: z.ZodString }>; | |
| z.object({ a: z.string() }).catchall(z.number()) satisfies z.ZodObject<{ a: z.ZodString }>; | |
| z.object({ a: z.string() }).strict() satisfies z.ZodObject; | |
| z.object({}) satisfies z.ZodObject; | |
| z.looseObject({ name: z.string() }) satisfies z.ZodObject< | |
| { | |
| name: z.ZodString; | |
| }, | |
| z.core.$loose | |
| >; | |
| z.looseObject({ name: z.string() }) satisfies z.ZodObject<{ | |
| name: z.ZodString; | |
| }>; | |
| z.strictObject({ name: z.string() }) satisfies z.ZodObject< | |
| { | |
| name: z.ZodString; | |
| }, | |
| z.core.$loose | |
| >; | |
| z.strictObject({ name: z.string() }) satisfies z.ZodObject< | |
| { | |
| name: z.ZodString; | |
| }, | |
| z.core.$strict | |
| >; | |
| z.object({ name: z.string() }) satisfies z.ZodObject<{ | |
| name: z.ZodString; | |
| }>; | |
| z.object({ | |
| a: z.string(), | |
| b: z.number(), | |
| c: z.boolean(), | |
| }) satisfies z.core.$ZodObject; | |
| }); | |
| test("null prototype", () => { | |
| const schema = z.object({ a: z.string() }); | |
| const obj = Object.create(null); | |
| obj.a = "foo"; | |
| expect(schema.parse(obj)).toEqual({ a: "foo" }); | |
| }); | |
| test("empty objects", () => { | |
| const A = z.looseObject({}); | |
| type Ain = z.input<typeof A>; | |
| expectTypeOf<Ain>().toEqualTypeOf<Record<string, unknown>>(); | |
| type Aout = z.output<typeof A>; | |
| expectTypeOf<Aout>().toEqualTypeOf<Record<string, unknown>>(); | |
| const B = z.object({}); | |
| type Bout = z.output<typeof B>; | |
| expectTypeOf<Bout>().toEqualTypeOf<Record<string, never>>(); | |
| type Bin = z.input<typeof B>; | |
| expectTypeOf<Bin>().toEqualTypeOf<Record<string, never>>(); | |
| const C = z.strictObject({}); | |
| type Cout = z.output<typeof C>; | |
| expectTypeOf<Cout>().toEqualTypeOf<Record<string, never>>(); | |
| type Cin = z.input<typeof C>; | |
| expectTypeOf<Cin>().toEqualTypeOf<Record<string, never>>(); | |
| }); | |
| test("preserve key order", () => { | |
| const schema = z.object({ | |
| a: z.string().optional(), | |
| b: z.string(), | |
| }); | |
| const r1 = schema.safeParse({ a: "asdf", b: "qwer" }); | |
| const r2 = schema.safeParse({ a: "asdf", b: "qwer" }, { jitless: true }); | |
| expect(Object.keys(r1.data!)).toMatchInlineSnapshot(` | |
| [ | |
| "a", | |
| "b", | |
| ] | |
| `); | |
| expect(Object.keys(r1.data!)).toEqual(Object.keys(r2.data!)); | |
| }); | |
| test("empty shape", () => { | |
| const a = z.object({}); | |
| a.parse({}); | |
| a.parse({}, { jitless: true }); | |
| a.parse(Object.create(null)); | |
| a.parse(Object.create(null), { jitless: true }); | |
| expect(() => a.parse([])).toThrow(); | |
| expect(() => a.parse([], { jitless: true })).toThrow(); | |
| }); | |
| test("zodtype assignability", () => { | |
| // Does not error | |
| z.object({ hello: z.string().optional() }) satisfies z.ZodType<{ hello?: string | undefined }>; | |
| z.object({ hello: z.string() }) satisfies z.ZodType<{ hello?: string | undefined }>; | |
| // @ts-expect-error | |
| z.object({}) satisfies z.ZodType<{ hello: string | undefined }>; | |
| // @ts-expect-error | |
| z.object({ hello: z.string().optional() }) satisfies z.ZodType<{ hello: string | undefined }>; | |
| // @ts-expect-error | |
| z.object({ hello: z.string().optional() }) satisfies z.ZodType<{ hello: string }>; | |
| // @ts-expect-error | |
| z.object({ hello: z.number() }) satisfies z.ZodType<{ hello?: string | undefined }>; | |
| }); | |
| test("index signature in shape", () => { | |
| function makeZodObj<const T extends string>(key: T) { | |
| return z.looseObject({ | |
| [key]: z.string(), | |
| }); | |
| } | |
| const schema = makeZodObj("foo"); | |
| type schema = z.infer<typeof schema>; | |
| expectTypeOf<schema>().toEqualTypeOf<Record<string, string>>(); | |
| }); | |
| test("extent() on object with refinements should throw", () => { | |
| const schema = z | |
| .object({ | |
| a: z.string(), | |
| }) | |
| .refine(() => true); | |
| expect(() => schema.extend({ b: z.string() })).toThrow(); | |
| }); | |
| test("safeExtend() on object with refinements should not throw", () => { | |
| const schema = z | |
| .object({ | |
| a: z.string(), | |
| }) | |
| .refine(() => true); | |
| expect(() => schema.safeExtend({ b: z.string() })).not.toThrow(); | |
| }); | |
Xet Storage Details
- Size:
- 17.3 kB
- Xet hash:
- ff40dbfa711bfc2644257e99b92744b06218c5211ebdf0a86698c356f9ee3aab
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.