I am doing this in Zod:
export const LoadSort: z.ZodType<LoadSort> = z.object({
name: z.string(),
tilt: z.enum(['+', '-']),
})
console.log(LoadSort.shape)
But getting an error on .shape
:
Property 'shape' does not exist on type 'ZodType<LoadSort, ZodTypeDef, LoadSort>'.ts(2339)
Actually, my code is more complex and requires me to use non-inferred types. Here is my full code
import { z } from 'zod'
export const LOAD_FIND_TEST = [
'bond',
'base_link_mark',
'head_link_mark',
'base_mark',
'head_mark',
'base_text',
'miss_bond',
'have_bond',
'have_text',
] as const
export type Load = {
find?: LoadFind
read?: LoadRead
save?: LoadSave
task?: string
}
export type LoadFind = LoadFindLink | Array<LoadFindLink>
export type LoadFindBind = {
form: 'bind'
list: Array<LoadFindLink>
}
export type LoadFindLike = {
base: LoadFindLikeLinkBond
form: 'like'
head: LoadFindLikeBond | LoadFindLikeLinkBond
test: LoadFindTest
}
export type LoadFindLikeBond = string | boolean | null | number
export type LoadFindLikeLinkBond = {
link: string
}
export type LoadFindLink = LoadFindLike | LoadFindRoll | LoadFindBind
export type LoadFindRoll = {
form: 'roll'
list: Array<LoadFindLink>
}
export type LoadFindTest = (typeof LOAD_FIND_TEST)[number]
export type LoadRead = {
[key: string]: true | LoadReadLink
}
export type LoadReadLink = {
find?: LoadFind
read: LoadRead
}
export type LoadSave = {
[key: string]: Array<LoadSaveBase> | LoadSaveBase
}
export type LoadSaveBase = {
find?: LoadFind
read?: LoadRead
save?: LoadSave
task?: string
}
export type LoadSort = {
name: string
tilt: '+' | '-'
}
export const Load: z.ZodType<Load> = z.object({
find: z.optional(z.lazy(() => LoadFind)),
read: z.optional(z.lazy(() => LoadRead)),
save: z.optional(z.lazy(() => LoadSave)),
task: z.optional(z.string()),
})
export const LoadFind: z.ZodType<LoadFind> = z.union([
z.lazy(() => LoadFindLink),
z.array(z.lazy(() => LoadFindLink)),
])
export const LoadRead: z.ZodType<LoadRead> = z.record(
z.union([z.lazy(() => LoadReadLink), z.literal(true)]),
)
export const LoadSave: z.ZodType<LoadSave> = z.record(
z.union(
z.array(z.lazy(() => LoadSaveBase)),
z.lazy(() => LoadSaveBase),
),
)
export const LoadFindBind: z.ZodType<LoadFindBind> = z.object({
form: z.literal('bind'),
list: z.array(z.lazy(() => LoadFindLink)),
})
export const LoadFindRoll: z.ZodType<LoadFindRoll> = z.object({
form: z.literal('roll'),
list: z.lazy(() => z.array(LoadFindLink)),
})
export const LoadFindTest = z.enum([
'bond',
'base_link_mark',
'head_link_mark',
'base_mark',
'head_mark',
'base_text',
'miss_bond',
'have_bond',
'have_text',
])
export const LoadFindLike: z.ZodType<LoadFindLike> = z.object({
base: z.lazy(() => LoadFindLikeLinkBond),
form: z.literal('like'),
head: z.union([
z.lazy(() => LoadFindLikeLinkBond),
z.lazy(() => LoadFindLikeBond),
]),
test: LoadFindTest,
})
export const LoadFindLink: z.ZodType<LoadFindLink> = z.union([
z.lazy(() => LoadFindLike),
z.lazy(() => LoadFindRoll),
z.lazy(() => LoadFindBind),
])
export const LoadFindLikeBond: z.ZodType<LoadFindLikeBond> = z.union([
z.string(),
z.boolean(),
z.null(),
z.number(),
])
export const LoadFindLikeLinkBond: z.ZodType<LoadFindLikeLinkBond> =
z.object({
link: z.string(),
})
export const LoadReadLink: z.ZodType<LoadReadLink> = z.object({
find: z.optional(LoadFind),
read: LoadRead,
})
export const LoadSaveBase: z.ZodType<LoadSaveBase> = z.object({
find: z.optional(LoadFind),
read: z.optional(LoadRead),
save: z.optional(LoadSave),
task: z.optional(z.string()),
})
export const LoadSort: z.ZodType<LoadSort> = z.object({
name: z.string(),
tilt: z.enum(['+', '-']),
})
How do I both specify the ZodType
and at the same time still get access to all the properties like .shape
that come on the inferred object?
I am able to sort of get it working with this hack, but it doesn't seem right:
export const LoadSort: z.ZodType<LoadSort> = z.object({
name: z.string(),
tilt: z.enum(['+', '-']),
})
assertZodObject(LoadSort)
for (const name in LoadSort.shape) {
const def = LoadSort.shape[name] as z.ZodType
console.log(def)
}
export function assertZodObject<S extends z.ZodObject<z.ZodRawShape>>(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
x: any,
): asserts x is S {
if (!(x instanceof z.ZodObject)) {
throw new Error()
}
}
Because you indicate that LoadSort
(the variable) is z.ZodType<LoadSort>
, and .shape
property is exist in ZodObject
, not ZodType
.
To access .shape
property and keep schema type checking, try to use satisfies
keyword:
const LoadSort = z.object({
name: z.string(),
tilt: z.enum(['+', '-']),
}) satisfies z.ZodType<LoadSort>
unrelated: don't use the same name on variable and interface, it may be confused.