-
Notifications
You must be signed in to change notification settings - Fork 3
feat(sql-orm-client): expand and simplify output types #283
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export type { SimplifyDeep } from '../simplify-deep'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| export type SimplifyDeep<T> = T extends readonly (infer Element)[] | ||
| ? T extends unknown[] | ||
| ? SimplifyDeep<Element>[] | ||
| : readonly SimplifyDeep<Element>[] | ||
| : T extends | ||
| | string | ||
| | number | ||
| | boolean | ||
| | bigint | ||
| | symbol | ||
| | Date | ||
| | RegExp | ||
| | Uint8Array | ||
| | ((...args: never[]) => unknown) | ||
| ? T | ||
| : T extends object | ||
| ? { [K in keyof T]: SimplifyDeep<T[K]> } | ||
| : T; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| import { describe, expectTypeOf, test } from 'vitest'; | ||
| import type { SimplifyDeep } from '../src/simplify-deep'; | ||
|
|
||
| describe('SimplifyDeep', () => { | ||
| test('primitives pass through', () => { | ||
| expectTypeOf<SimplifyDeep<string>>().toEqualTypeOf<string>(); | ||
| expectTypeOf<SimplifyDeep<number>>().toEqualTypeOf<number>(); | ||
| expectTypeOf<SimplifyDeep<boolean>>().toEqualTypeOf<boolean>(); | ||
| expectTypeOf<SimplifyDeep<bigint>>().toEqualTypeOf<bigint>(); | ||
| expectTypeOf<SimplifyDeep<symbol>>().toEqualTypeOf<symbol>(); | ||
| expectTypeOf<SimplifyDeep<null>>().toEqualTypeOf<null>(); | ||
| expectTypeOf<SimplifyDeep<undefined>>().toEqualTypeOf<undefined>(); | ||
| expectTypeOf<SimplifyDeep<unknown>>().toEqualTypeOf<unknown>(); | ||
| expectTypeOf<SimplifyDeep<never>>().toEqualTypeOf<never>(); | ||
| }); | ||
|
|
||
| test('branded primitives pass through', () => { | ||
| type Branded = string & { readonly __brand: true }; | ||
| expectTypeOf<SimplifyDeep<Branded>>().toEqualTypeOf<Branded>(); | ||
| }); | ||
|
|
||
| test('Date, RegExp, and Uint8Array preserved', () => { | ||
| expectTypeOf<SimplifyDeep<Date>>().toEqualTypeOf<Date>(); | ||
| expectTypeOf<SimplifyDeep<RegExp>>().toEqualTypeOf<RegExp>(); | ||
| expectTypeOf<SimplifyDeep<Uint8Array>>().toEqualTypeOf<Uint8Array>(); | ||
| }); | ||
|
|
||
| test('functions preserved', () => { | ||
| type Fn = (a: number, b: string) => boolean; | ||
| expectTypeOf<SimplifyDeep<Fn>>().toEqualTypeOf<Fn>(); | ||
| }); | ||
|
|
||
| test('intersections flatten into plain objects', () => { | ||
| type Input = { a: number } & { b: string }; | ||
| type Expected = { a: number; b: string }; | ||
| expectTypeOf<SimplifyDeep<Input>>().toEqualTypeOf<Expected>(); | ||
| }); | ||
|
|
||
| test('mutable arrays recurse', () => { | ||
| type Input = ({ a: number } & { b: string })[]; | ||
| type Expected = { a: number; b: string }[]; | ||
| expectTypeOf<SimplifyDeep<Input>>().toEqualTypeOf<Expected>(); | ||
| }); | ||
|
|
||
| test('readonly arrays preserve readonly', () => { | ||
| type Input = readonly ({ a: number } & { b: string })[]; | ||
| type Expected = readonly { a: number; b: string }[]; | ||
| expectTypeOf<SimplifyDeep<Input>>().toEqualTypeOf<Expected>(); | ||
| }); | ||
|
|
||
| test('nested objects recurse', () => { | ||
| type Input = { nested: { a: number } & { b: string } }; | ||
| type Expected = { nested: { a: number; b: string } }; | ||
| expectTypeOf<SimplifyDeep<Input>>().toEqualTypeOf<Expected>(); | ||
| }); | ||
|
|
||
| test('nullable objects', () => { | ||
| type Input = ({ a: number } & { b: string }) | null; | ||
| type Expected = { a: number; b: string } | null; | ||
| expectTypeOf<SimplifyDeep<Input>>().toEqualTypeOf<Expected>(); | ||
| }); | ||
|
|
||
| test('nested arrays of intersected objects', () => { | ||
| type Input = { | ||
| items: ({ id: number } & { name: string })[]; | ||
| }; | ||
| type Expected = { | ||
| items: { id: number; name: string }[]; | ||
| }; | ||
| expectTypeOf<SimplifyDeep<Input>>().toEqualTypeOf<Expected>(); | ||
| }); | ||
|
|
||
| test('bidirectional assignability for concrete types', () => { | ||
| type Original = { a: number } & { b: string; nested: { c: boolean } & { d: number } }; | ||
| type Simplified = SimplifyDeep<Original>; | ||
|
|
||
| expectTypeOf<Original>().toExtend<Simplified>(); | ||
| expectTypeOf<Simplified>().toExtend<Original>(); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ import { | |
| type ToWhereExpr, | ||
| type WhereArg, | ||
| } from '@prisma-next/sql-relational-core/ast'; | ||
| import type { SimplifyDeep } from '@prisma-next/utils/simplify-deep'; | ||
| import { createAggregateBuilder, isAggregateSelector } from './aggregate-builder'; | ||
| import { normalizeAggregateResult } from './collection-aggregate-result'; | ||
| import { mapCursorValuesToColumns, mapFieldsToColumns } from './collection-column-mapping'; | ||
|
|
@@ -159,7 +160,7 @@ interface MtiCreateContext { | |
| export class Collection< | ||
| TContract extends Contract<SqlStorage>, | ||
| ModelName extends string, | ||
| Row = InferRootRow<TContract, ModelName>, | ||
| Row = SimplifyDeep<InferRootRow<TContract, ModelName>>, | ||
| State extends CollectionTypeState = DefaultCollectionTypeState, | ||
|
Comment on lines
160
to
164
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. F01 — Blocking: The default Suggestion: Apply
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Each SimplifyDeep<
Row & {
Also, if you defer to terminal methods, then hovering over any intermediate builder step shows raw intersections like |
||
| > implements RowSelection<Row> | ||
| { | ||
|
|
@@ -317,15 +318,17 @@ export class Collection< | |
| ): Collection< | ||
| TContract, | ||
| ModelName, | ||
| Row & { | ||
| [K in RelName]: IncludeRefinementValue< | ||
| TContract, | ||
| ModelName, | ||
| K, | ||
| DefaultModelRow<TContract, RelatedName>, | ||
| RefinedResult | ||
| >; | ||
| }, | ||
| SimplifyDeep< | ||
| Row & { | ||
| [K in RelName]: IncludeRefinementValue< | ||
| TContract, | ||
| ModelName, | ||
| K, | ||
| DefaultModelRow<TContract, RelatedName>, | ||
| RefinedResult | ||
| >; | ||
| } | ||
| >, | ||
| State | ||
| > { | ||
| const relation = resolveIncludeRelation(this.contract, this.modelName, relationName as string); | ||
|
|
@@ -391,15 +394,17 @@ export class Collection< | |
| }; | ||
|
|
||
| return this.#cloneWithRow< | ||
| Row & { | ||
| [K in RelName]: IncludeRefinementValue< | ||
| TContract, | ||
| ModelName, | ||
| K, | ||
| DefaultModelRow<TContract, RelatedName>, | ||
| RefinedResult | ||
| >; | ||
| }, | ||
| SimplifyDeep< | ||
| Row & { | ||
| [K in RelName]: IncludeRefinementValue< | ||
| TContract, | ||
| ModelName, | ||
| K, | ||
| DefaultModelRow<TContract, RelatedName>, | ||
| RefinedResult | ||
| >; | ||
| } | ||
| >, | ||
| State | ||
| >({ | ||
| includes: [...this.state.includes, includeExpr], | ||
|
|
@@ -416,15 +421,19 @@ export class Collection< | |
| ): Collection< | ||
| TContract, | ||
| ModelName, | ||
| Pick<DefaultModelRow<TContract, ModelName>, Fields[number]> & | ||
| IncludedRelationsForRow<TContract, ModelName, Row>, | ||
| SimplifyDeep< | ||
| Pick<DefaultModelRow<TContract, ModelName>, Fields[number]> & | ||
| IncludedRelationsForRow<TContract, ModelName, Row> | ||
| >, | ||
| State | ||
| > { | ||
| const selectedFields = mapFieldsToColumns(this.contract, this.modelName, fields); | ||
|
|
||
| return this.#cloneWithRow< | ||
| Pick<DefaultModelRow<TContract, ModelName>, Fields[number]> & | ||
| IncludedRelationsForRow<TContract, ModelName, Row>, | ||
| SimplifyDeep< | ||
| Pick<DefaultModelRow<TContract, ModelName>, Fields[number]> & | ||
| IncludedRelationsForRow<TContract, ModelName, Row> | ||
| >, | ||
| State | ||
| >({ | ||
| selectedFields, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: prisma/prisma-next
Length of output: 604
🏁 Script executed:
Repository: prisma/prisma-next
Length of output: 474
Use the documented workaround pattern for
nevertype assertions.Line 14 uses
toEqualTypeOf<never>(), which does not compile with the current Vitest version. Replace with the value-form workaround as already used in the integration test suite.Proposed fix
Note: The same issue exists in
packages/2-mongo-family/1-foundation/mongo-codec/test/codecs.test-d.ts:51and should be fixed with the same pattern.📝 Committable suggestion
🤖 Prompt for AI Agents