TypeScript is not able to resolve recursive Zod schema

1 day ago 3
ARTICLE AD BOX

Given a library exporting Zod schemas (living in their own separate files) defining a recursive table header structure.

Starting with the root schema

const columnsConfigurationSchema = z.array(columnConfigurationSchema).min(1);

where each element is either a group or a leaf

const columnConfigurationSchema = z.union([ columnGroupConfigurationSchema, leafColumnConfigurationSchema, ]);

I also added a helper function to check if a column is a column group

function isColumnGroup(column: ColumnConfiguration): column is ColumnGroupConfiguration { return "children" in column; }

A column group contains some group fields and child columns, so things start getting recursive

const columnGroupConfigurationSchema = z.object({ // ...group fields... get children() { return columnsConfigurationSchema; }, });

A leaf column can be of multiple types (based on field type) but this is just an example

const leafColumnConfigurationSchema = z.discriminatedUnion("type", [ workItemColumnConfigurationSchema, computedColumnConfigurationSchema, ]);

where a WorkItemColumn represents

const workItemColumnConfigurationSchema = z.object({ // type field (z.ZodLiteral<"workItem">) // other fields });

and a ComputedColumn represents

const computedColumnConfigurationSchema = z.object({ // type field (z.ZodLiteral<"computed">) // other fields });

Since I'm using the getter property TypeScript and Zod are able to handle the recursive schema. But when consuming the library at some places TypeScript doesn't know how to resolve the types correctly. For example

function getColspan(columnConfiguration: ColumnConfiguration) { if(!isColumnGroup(columnConfiguration)) { return 1; } // children should not be of type any here return columnConfiguration.children.reduce((colspanSum, currentChildColumn) => colspanSum + getColspan(currentChildColumn), 0); }

The type guard checks if a given element is a column group. But still the children field is of type `any` although I would expect it to be of type `ColumnsConfiguration`.

How to fix the schema so TypeScript knows how to handle it correctly?

Read Entire Article