zod
zod copied to clipboard
Recursive Tuple not working as expected
Hey all, sorry to bother you! I have a bit of a head scratcher here (this could just be a lack of typescript foo) I've been trying to do a recursive tuple using Zod and when I run my script I am getting the following error:
return new TSError(diagnosticText, diagnosticCodes, diagnostics);
^
{ TSError: ⨯ Unable to compile TypeScript:
examples/Markdown.ts:87:14 - error TS2322: Type 'ZodLazy<ZodTuple<[ZodUnion<[ZodNativeEnum<{ readonly A: "a"; readonly ABBR: "abbr"; readonly ACRONYM: "acronym"; readonly B: "b"; readonly BDO: "bdo"; readonly BI: "big"; readonly BR: "br"; readonly BUTTON: "button"; ... 24 more ...; readonly VAR: "var"; }>, ZodNativeEnum<...>]>, ZodObject<...>, ZodNullable<...>], n...' is not assignable to type 'ZodType<Markdown, ZodTypeDef, Markdown>'.
Types of property '_type' are incompatible.
Type '["object" | "map" | "code" | "address" | "article" | "aside" | "blockquote" | "canvas" | "dd" | "div" | "dl" | "dt" | "fieldset" | "figcaption" | "figure" | "footer" | "form" | ... 49 more ... | "var", {}, Markdown[], ...unknown[]]' is not assignable to type 'Markdown'.
Target allows only 3 element(s) but source may have more.
87 export const Markdown: z.ZodType<Markdown> = z.lazy(() =
This is my actual code implementation: Markdown.ts
// TAGS has been simplified here for readability
const TAGS = { BR: "br", P: "p", SPAN: "span" } as const
type Children = string | Markdown
type Markdown = [
typeof TAGS[keyof typeof TAGS],
{},
Children[] | null
]
export const Markdown: z.ZodType<Markdown> = z.lazy(() =>
z.tuple([
z.nativeEnum(TAGS),
z.object({}),
z.nullable(z.array(Markdown))
])
)
// This typing works:
const sample: Markdown = [
"p",
{},
[
"Let us know how we can help!",
[
"br",
{},
null
],
[
"span",
{},
[
"Hello Jim!"
]
]
]
]
I then have a little node script that I run to take the schemas and generate JSON Schemas from this and that is where the error is thrown, but it appears to be an actual Typescript error. The Markdown.ts file doesn't show any Typescript errors, the type that I use with the sample
data seems to work a okay, and this error appears at run time. I'm not sure if this is maybe me not groking Typescript tuples well enough or if this should theoretically be working and there is something not aligning on the zod side of things.
Wanted to give an update on this, so I am able to see the error in my editor now with the following code:
const TAGS = {...BlockElements, ...InlineElements, } as const
type Markdown = [typeof TAGS[keyof typeof TAGS], {}, Children[] | null]
type Children = string | Markdown
export const Markdown: ZodType<Markdown> = z.tuple([
z.nativeEnum(TAGS),
z.object({}),
z.array<ZodType<Markdown>>(z.lazy(() => Markdown))
])
I think you need this:
Markdown.or( z.string() )
Here's the full example:
// TAGS has been simplified here for readability
const TAGS = { BR: "br", P: "p", SPAN: "span" } as const
type Children = string | Markdown
type Markdown = [
typeof TAGS[ keyof typeof TAGS ],
{},
Children[] | null
]
const Markdown: z.ZodType<Markdown> = z.lazy( () =>
z.tuple( [
z.nativeEnum( TAGS ),
z.object( {} ),
z.nullable( z.array( Markdown.or( z.string() ) ) )
] )
)
// This typing works:
const sample: Markdown = [
"p",
{},
[
"Let us know how we can help!",
[
"br",
{},
null
],
[
"span",
{},
[
"Hello Jim!"
]
]
]
]
console.log( Markdown.safeParse( sample ).success ) // true