From 08df50cc05835fec97f7a9aeeb76e73606637cb7 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Sun, 13 Jul 2025 15:03:57 +0800 Subject: [PATCH 1/3] fix: nested select result typing, simplifying ORM query result types --- packages/runtime/src/client/contract.ts | 30 +++++++------- packages/runtime/src/client/crud-types.ts | 22 ++++++---- packages/runtime/src/utils/type-utils.ts | 4 +- packages/runtime/test/typing/verify-typing.ts | 41 ++++++++++++++++++- 4 files changed, 72 insertions(+), 25 deletions(-) diff --git a/packages/runtime/src/client/contract.ts b/packages/runtime/src/client/contract.ts index 531f3fa7..53c8a36f 100644 --- a/packages/runtime/src/client/contract.ts +++ b/packages/runtime/src/client/contract.ts @@ -1,7 +1,7 @@ import type { Decimal } from 'decimal.js'; import { type GetModels, type ProcedureDef, type SchemaDef } from '../schema'; import type { AuthType } from '../schema/auth'; -import type { OrUndefinedIf, UnwrapTuplePromises } from '../utils/type-utils'; +import type { OrUndefinedIf, Simplify, UnwrapTuplePromises } from '../utils/type-utils'; import type { TRANSACTION_UNSUPPORTED_METHODS } from './constants'; import type { AggregateArgs, @@ -299,7 +299,7 @@ export interface ModelOperations>( args?: SelectSubset>, - ): ZenStackPromise[]>; + ): ZenStackPromise>[]>; /** * Returns a uniquely identified entity. @@ -309,7 +309,7 @@ export interface ModelOperations>( args?: SelectSubset>, - ): ZenStackPromise | null>; + ): ZenStackPromise> | null>; /** * Returns a uniquely identified entity or throws `NotFoundError` if not found. @@ -319,7 +319,7 @@ export interface ModelOperations>( args?: SelectSubset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Returns the first entity. @@ -329,7 +329,7 @@ export interface ModelOperations>( args?: SelectSubset>, - ): ZenStackPromise | null>; + ): ZenStackPromise> | null>; /** * Returns the first entity or throws `NotFoundError` if not found. @@ -339,7 +339,7 @@ export interface ModelOperations>( args?: SelectSubset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Creates a new entity. @@ -395,7 +395,7 @@ export interface ModelOperations>( args: SelectSubset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Creates multiple entities. Only scalar fields are allowed. @@ -446,7 +446,7 @@ export interface ModelOperations>( args?: SelectSubset>, - ): ZenStackPromise[]>; + ): ZenStackPromise>[]>; /** * Updates a uniquely identified entity. @@ -567,7 +567,7 @@ export interface ModelOperations>( args: SelectSubset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Updates multiple entities. @@ -617,7 +617,7 @@ export interface ModelOperations>( args: Subset>, - ): ZenStackPromise[]>; + ): ZenStackPromise>[]>; /** * Creates or updates an entity. @@ -641,7 +641,7 @@ export interface ModelOperations>( args: SelectSubset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Deletes a uniquely identifiable entity. @@ -664,7 +664,7 @@ export interface ModelOperations>( args: SelectSubset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Deletes multiple entities. @@ -709,7 +709,7 @@ export interface ModelOperations>( args?: Subset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Aggregates rows. @@ -730,7 +730,7 @@ export interface ModelOperations>( args: Subset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Groups rows by columns. @@ -766,7 +766,7 @@ export interface ModelOperations>( args: Subset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; } //#endregion diff --git a/packages/runtime/src/client/crud-types.ts b/packages/runtime/src/client/crud-types.ts index 4643991d..259e1023 100644 --- a/packages/runtime/src/client/crud-types.ts +++ b/packages/runtime/src/client/crud-types.ts @@ -77,13 +77,21 @@ type ModelSelectResult RelationFieldType, FieldIsArray > - ? ModelResult< - Schema, - RelationFieldType, - Select[Key], - FieldIsOptional, - FieldIsArray - > + ? 'select' extends keyof Select[Key] + ? ModelResult< + Schema, + RelationFieldType, + Pick, + FieldIsOptional, + FieldIsArray + > + : ModelResult< + Schema, + RelationFieldType, + Pick, + FieldIsOptional, + FieldIsArray + > : DefaultModelResult< Schema, RelationFieldType, diff --git a/packages/runtime/src/utils/type-utils.ts b/packages/runtime/src/utils/type-utils.ts index abd963a5..ff9d441e 100644 --- a/packages/runtime/src/utils/type-utils.ts +++ b/packages/runtime/src/utils/type-utils.ts @@ -6,6 +6,8 @@ export type NullableIf = Condition extends true ? export type PartialRecord = Partial>; +export type Simplify = T extends object ? { [K in keyof T]: Simplify } & {} : T; + export type WrapType = Optional extends true ? T | null : Array extends true @@ -33,8 +35,6 @@ export type JsonValue = string | number | boolean | null | JsonObject | JsonArra export type JsonObject = { [key: string]: JsonValue }; export type JsonArray = Array; -export type Simplify = { [Key in keyof T]: T[Key] } & {}; - export function call(code: string) { return { code }; } diff --git a/packages/runtime/test/typing/verify-typing.ts b/packages/runtime/test/typing/verify-typing.ts index e616140e..86389616 100644 --- a/packages/runtime/test/typing/verify-typing.ts +++ b/packages/runtime/test/typing/verify-typing.ts @@ -1,7 +1,7 @@ +import SQLite from 'better-sqlite3'; import { ZenStackClient } from '../../dist'; import { Role } from './models'; import { schema } from './schema'; -import SQLite from 'better-sqlite3'; const client = new ZenStackClient(schema, { dialectConfig: { @@ -147,6 +147,45 @@ async function find() { }, }) ).profile?.region?.city; + + ( + await client.user.findFirstOrThrow({ + select: { + posts: { + where: { title: 'Foo' }, + select: { + author: { + select: { + id: true, + }, + }, + }, + }, + }, + }) + ).posts[0]?.author?.id; + + const u = await client.user.findFirstOrThrow({ + select: { + posts: { + where: { title: 'Foo' }, + select: { + author: { + include: { + profile: true, + }, + omit: { + email: true, + }, + }, + }, + }, + }, + }); + console.log(u.posts[0]?.author?.profile?.age); + console.log(u.posts[0]?.author?.role); + // @ts-expect-error + console.log(u.posts[0]?.author?.email); } async function create() { From 1ee12096c253c6627b056e25474350ee7fc21b63 Mon Sep 17 00:00:00 2001 From: Yiming Cao Date: Sun, 13 Jul 2025 15:07:47 +0800 Subject: [PATCH 2/3] Update packages/runtime/src/utils/type-utils.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/runtime/src/utils/type-utils.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/runtime/src/utils/type-utils.ts b/packages/runtime/src/utils/type-utils.ts index ff9d441e..26a0f2e6 100644 --- a/packages/runtime/src/utils/type-utils.ts +++ b/packages/runtime/src/utils/type-utils.ts @@ -6,7 +6,13 @@ export type NullableIf = Condition extends true ? export type PartialRecord = Partial>; -export type Simplify = T extends object ? { [K in keyof T]: Simplify } & {} : T; +type Depth = [never, ...Depth]; +export type Simplify = + D extends [never, ...infer Rest] + ? T extends object + ? { [K in keyof T]: Simplify } & {} + : T + : T; export type WrapType = Optional extends true ? T | null From d9a8c3b69e77db514715d909768c013520c642bc Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Sun, 13 Jul 2025 15:20:20 +0800 Subject: [PATCH 3/3] update --- packages/runtime/src/utils/type-utils.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/runtime/src/utils/type-utils.ts b/packages/runtime/src/utils/type-utils.ts index 26a0f2e6..6769f177 100644 --- a/packages/runtime/src/utils/type-utils.ts +++ b/packages/runtime/src/utils/type-utils.ts @@ -6,13 +6,15 @@ export type NullableIf = Condition extends true ? export type PartialRecord = Partial>; -type Depth = [never, ...Depth]; -export type Simplify = - D extends [never, ...infer Rest] - ? T extends object - ? { [K in keyof T]: Simplify } & {} - : T - : T; +type _Preserve = Date | Function | Decimal | Uint8Array; +type _Depth = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; +export type Simplify = D extends 0 + ? T + : T extends object + ? T extends _Preserve + ? T + : { [K in keyof T]: Simplify } & {} + : T; export type WrapType = Optional extends true ? T | null @@ -32,9 +34,11 @@ export type MapBaseType = T extends 'String' ? Decimal : T extends 'DateTime' ? Date - : T extends 'Json' - ? JsonValue - : unknown; + : T extends 'Bytes' + ? Uint8Array + : T extends 'Json' + ? JsonValue + : unknown; export type JsonValue = string | number | boolean | null | JsonObject | JsonArray;