-
Notifications
You must be signed in to change notification settings - Fork 0
/
zod.ts
101 lines (85 loc) · 2.67 KB
/
zod.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import { z } from "https://deno.land/x/[email protected]/mod.ts"
/**
* Coerce and parse object data.
*
* @param data
* @param schema
* @returns
*/
export function safeCoerceObject<T extends z.ZodObject<z.ZodRawShape>>(
data: unknown,
schema: T,
): z.SafeParseError<unknown> | z.SafeParseSuccess<z.TypeOf<T>> {
if (typeof data !== "object") {
throw Error(`Expected data of type object: Received ${typeof data}`)
}
if (schema._def.typeName !== "ZodObject") {
throw Error(
`Expected schema of type ZodObject: Received ${schema._def.typeName}`,
)
}
const entries = Object.entries(data as object)
const parsedEntries = entries
.map(([key, value]) => {
const valueSchema = schema.shape[key]
let parsed = valueSchema?.safeParse(value) ?? z.any().safeParse(value)
if (!parsed.success) {
parsed = typeof value === "object" && !(value instanceof Date)
? safeCoerceObject(value, valueSchema as z.ZodObject<z.ZodRawShape>)
: safeCoercePrimitive(value, valueSchema)
}
return [key, parsed] as const
})
const entry = parsedEntries.find(([_, parsed]) => !parsed.success)
if (entry) {
const error = entry[1] as z.SafeParseError<unknown>
return error
}
const coercedSearchParams = Object.fromEntries(
parsedEntries.map((
[key, parsed],
) => [key, parsed.success ? parsed.data : null]),
)
return schema.safeParse(coercedSearchParams)
}
/**
* Coerce and parse primitive data.
*
* @param data - Data to be coerced and parsed according to schema.
* @param schema - Primitive Zod schema.
* @returns A parse result or error.
*/
export function safeCoercePrimitive<
const T extends number | boolean | bigint | string | Date,
>(
data: unknown,
schema: z.ZodType<T>,
): z.SafeParseReturnType<unknown, T> {
// deno-lint-ignore no-explicit-any
const schemaType = (schema._def as any).typeName
const coerceSchema = schemaType === "ZodNumber"
? z.coerce.number()
: schemaType === "ZodBoolean"
? z.coerce.boolean()
: schemaType === "ZodBigInt"
? z.coerce.bigint()
: schemaType === "ZodString"
? z.coerce.string()
: schemaType === "ZodDate"
? z.coerce.date()
: null
// If no coerce schema selected, throw error
if (!coerceSchema) {
throw new Error(
`Expected schema type of (ZodNumber, ZodBoolean, ZodBigInt, ZodString, ZodDate): Received ${schemaType}`,
)
}
// Parse using coerce schema
const coerceParsed = coerceSchema.safeParse(data)
// Return error if not successful
if (!coerceParsed.success) {
return coerceParsed
}
// Return parse result of schema on coerced data
return schema.safeParse(coerceParsed.data)
}