-
-
Notifications
You must be signed in to change notification settings - Fork 138
(v3) Custom JSON - typescript errors when working with null values #2411
Copy link
Copy link
Open
Labels
Description
Description and expected behavior
im experiencing typescript errors when working with nullable custom json types
i followed the docs here:
https://zenstack.dev/docs/orm/typed-json
https://zenstack.dev/docs/orm/api/json-null
using the latest version of v3 (^3.3.3) but experiencing this across older versions as well. see the test script below i made where i try to perform create/update/find operations for a nullable vs non-nullable json column:
type Metadata {
someString String
someInt Int
}
model Foo {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
metadata Metadata @json
optionalMetadata Metadata? @json
}
import { AnyNull, DbNull, JsonNull } from "@zenstackhq/orm";
import { db } from "./db";
import type { Metadata } from "@/zenstack/models";
/* References:
- https://zenstack.dev/docs/orm/api/json-null
- https://zenstack.dev/docs/orm/typed-json
- https://github.com/zenstackhq/zenstack/issues/2278 (Maybe related (from v2))
*/
export const testJsonNulls = async () => {
/* --------------------------------- CREATE --------------------------------- */
const metadata: Metadata = { someInt: 1, someString: "test" };
// metadata (non nullable)
// @ts-expect-error - should not be able to set a null value to the non nullable field
await db.foo.create({ data: { metadata: DbNull } });
// @ts-expect-error - should not be able to set a null value to the non nullable field
await db.foo.create({ data: { metadata: JsonNull } });
// @ts-expect-error - should not be able to set a null value to the non nullable field
await db.foo.create({ data: { metadata: AnyNull } });
// @ts-expect-error - should not be able to set a null value to the non nullable field
await db.foo.create({ data: { metadata: null } });
await db.foo.create({ data: { metadata } }); // ✅ No typescript error
// optionalMetadata (nullable)
/*
Type 'DbNullClass' is not assignable to type 'Omit<{ someInt: number; someString: string; }, never> & Partial<Pick<{ someInt: number; someString: string; }, never>> & Record<string, unknown>'.
Type 'DbNullClass' is missing the following properties from type 'Omit<{ someInt: number; someString: string; }, never>': someInt, someString
*/
await db.foo.create({ data: { metadata, optionalMetadata: DbNull } }); // ❌ typescript error
await db.foo.create({ data: { metadata, optionalMetadata: JsonNull } }); // ❌ typescript error
await db.foo.create({ data: { metadata, optionalMetadata: AnyNull } }); // ❌ typescript error
await db.foo.create({ data: { metadata, optionalMetadata: null } }); // ✅ No typescript error
/* --------------------------------- UPDATE --------------------------------- */
const firstFoo = await db.foo.findFirst();
if (firstFoo) {
// metadata (non nullable)
const where = { id: firstFoo.id };
// @ts-expect-error - should not be able to set a null value to the non nullable field
await db.foo.update({ where, data: { metadata: DbNull } });
// @ts-expect-error - should not be able to set a null value to the non nullable field
await db.foo.update({ where, data: { metadata: JsonNull } });
// @ts-expect-error - should not be able to set a null value to the non nullable field
await db.foo.update({ where, data: { metadata: AnyNull } });
// @ts-expect-error - should not be able to set a null value to the non nullable field
await db.foo.update({ where, data: { metadata: null } });
await db.foo.update({ where, data: { metadata } }); // ✅ No typescript error
// optionalMetadata (nullable)
await db.foo.update({ where, data: { metadata, optionalMetadata: DbNull } }); // ❌ typescript error
await db.foo.update({ where, data: { metadata, optionalMetadata: JsonNull } }); // ❌ typescript error
await db.foo.update({ where, data: { metadata, optionalMetadata: AnyNull } }); // ❌ typescript error
await db.foo.update({ where, data: { metadata, optionalMetadata: null } }); // ✅ No typescript error
}
/* ---------------------------------- FIND ---------------------------------- */
// metadata (non nullable)
// @ts-expect-error - should not be able to filter by DbNull on a non nullable field
await db.foo.findMany({ where: { metadata: DbNull } });
// @ts-expect-error - should not be able to filter by JsonNull on a non nullable field
await db.foo.findMany({ where: { metadata: JsonNull } });
// @ts-expect-error - should not be able to filter by AnyNull on a non nullable field
await db.foo.findMany({ where: { metadata: AnyNull } });
// @ts-expect-error - should not be able to filter by null on a non nullable field
await db.foo.findMany({ where: { metadata: null } });
// optionalMetadata (nullable)
/*
Type 'DbNullClass' is not assignable to type '(Without<JsonFilter, TypedJsonFieldsFilter<SchemaType, "Metadata">> & TypedJsonFieldsFilter<SchemaType, "Metadata">) | ... 5 more ... | undefined'.ts(2322)
*/
await db.foo.findMany({ where: { optionalMetadata: DbNull } }); // ❌ typescript error
await db.foo.findMany({ where: { optionalMetadata: JsonNull } }); // ❌ typescript error
await db.foo.findMany({ where: { optionalMetadata: AnyNull } }); // ❌ typescript error
await db.foo.findMany({ where: { optionalMetadata: null } }); // ✅ No typescript error
};also related to working with nulls, im wondering how this would work using a frontend query client like tanstack. looks like an issue was created around this for V2: #2278
example:
const client = useClientQueries(schema);
// No typescript error, but will only find rows with db null values
const query = client.foo.useFindMany({ where: { optionalMetadata: null } });
const mutation = client.foo.useUpdate();
const performMutation = async (id: string) => {
// No typescript error, but sets the value to json null, not db null.
// The above query will not find return the updated row because the stored null value does not match the json null value.
await mutation.mutate({ where: { id }, data: { optionalMetadata: null } });
};Environment (please complete the following information):
- ZenStack version: 3.3.3
- Database type: Postgresql
- Node.js/Bun version: 24.12.0
- Package manager: bun
Additional context
link to discord message
Reactions are currently unavailable