Problem with infering json type in zod schema

When using the following table/schema:
const Test = mysqlTable(
"Test",
{
id: serial("id").primaryKey().notNull(),
value: json("value").$type<{ a: string, b: number }>().notNull()
},
)

const TestInsertSchema = createInsertSchema(Test)

const ValueFromSchema = TestInsertSchema.pick({
value: true
})

type Value = z.infer<typeof ValueFromSchema>
// Value is typed as following:
// type Value = {
// value: ((string | number | boolean | {
// [key: string]: Json;
// } | Json[]) & (string | number | boolean | {
// [key: string]: Json;
// } | Json[] | undefined)) | null;
// }
const Test = mysqlTable(
"Test",
{
id: serial("id").primaryKey().notNull(),
value: json("value").$type<{ a: string, b: number }>().notNull()
},
)

const TestInsertSchema = createInsertSchema(Test)

const ValueFromSchema = TestInsertSchema.pick({
value: true
})

type Value = z.infer<typeof ValueFromSchema>
// Value is typed as following:
// type Value = {
// value: ((string | number | boolean | {
// [key: string]: Json;
// } | Json[]) & (string | number | boolean | {
// [key: string]: Json;
// } | Json[] | undefined)) | null;
// }
Am I doing something wrong? How can I pick the value key with correct types from the schema?
Solution:
The .$type() helper only works on the type level, it cannot change the runtime behavior. The schema validation happens at runtime, thus it doesn't know about your type. If you want to validate your json field according to your type, you would need to refine the insert schema, like this: ```ts const TestInsertSchema = createInsertSchema, { value: () => z.object({ a: z.string(), b: z.number() }), });...
Jump to solution
1 Reply
Solution
Dan
Dan2y ago
The .$type() helper only works on the type level, it cannot change the runtime behavior. The schema validation happens at runtime, thus it doesn't know about your type. If you want to validate your json field according to your type, you would need to refine the insert schema, like this:
const TestInsertSchema = createInsertSchema, {
value: () => z.object({ a: z.string(), b: z.number() }),
});
const TestInsertSchema = createInsertSchema, {
value: () => z.object({ a: z.string(), b: z.number() }),
});
To make it a bit more convenient, you can extract the object schema into a variable and reuse it:
const valueSchema = z.object({ a: z.string(), b: z.number() });

const Test = mysqlTable(
"Test",
{
id: serial("id").primaryKey().notNull(),
value: json("value").$type<z.infer<typeof valueSchema>>().notNull()
},
);

const TestInsertSchema = createInsertSchema, {
value: () => valueSchema,
});
const valueSchema = z.object({ a: z.string(), b: z.number() });

const Test = mysqlTable(
"Test",
{
id: serial("id").primaryKey().notNull(),
value: json("value").$type<z.infer<typeof valueSchema>>().notNull()
},
);

const TestInsertSchema = createInsertSchema, {
value: () => valueSchema,
});
Want results from more Discord servers?
Add your server