This solution adds full type-safety support for virtual fields in Prisma select types and GetPayload helpers. Users can now select virtual fields in type-safe queries and get properly typed results.
File: packages/cli/src/generator/types.ts
Generated a new GetPayload helper type for each list that:
- Extends Prisma's
GetPayloadtype - Conditionally includes virtual fields based on selection
- Handles
selectandincludemodes correctly - Only includes selected virtual fields (not all virtual fields)
Example Generated Type:
export type UserGetPayload<T extends { select?: any; include?: any } = {}> = Omit<
Prisma.UserGetPayload<T>,
'password'
> &
UserTransformedFields &
(T extends { select: any }
? T['select'] extends true
? UserVirtualFields
: {
[K in keyof UserVirtualFields as K extends keyof T['select']
? T['select'][K] extends true
? K
: never
: never]: UserVirtualFields[K]
}
: T extends { include: any }
? T['include'] extends true
? UserVirtualFields
: {
[K in keyof UserVirtualFields as K extends keyof T['include']
? T['include'][K] extends true
? K
: never
: never]: UserVirtualFields[K]
}
: UserVirtualFields)File: packages/cli/src/generator/types.ts
Added generateGetPayloadType() function that:
- Checks if list has virtual or transformed fields
- Generates conditional type logic for selective field inclusion
- Includes comprehensive JSDoc documentation
- Skips generation if no virtual/transformed fields exist
File: packages/cli/src/generator/types.test.ts
Added two new test cases:
should generate Select and GetPayload types with virtual fieldsshould generate Include type with virtual fields for models with relationships
Both tests verify:
{ListName}VirtualFieldstype is generated{ListName}SelectextendsPrisma.{ListName}Selectwith virtual fields{ListName}GetPayloadhelper type is generated- Snapshots capture the complete generated output
import { Prisma } from '@/.opensaas/prisma-client/client'
const studentDetailSelect = {
id: true,
firstName: true,
age: true, // ❌ TS Error: 'age' does not exist in type 'StudentSelect'
} satisfies Prisma.StudentSelect
type StudentDetail = Prisma.StudentGetPayload<{
select: typeof studentDetailSelect
}>
// ❌ StudentDetail doesn't include 'age' even if type error is ignoredimport { StudentSelect, StudentGetPayload } from '@/.opensaas/types'
const studentDetailSelect = {
id: true,
firstName: true,
age: true, // ✅ Virtual field - TypeScript knows about this!
} satisfies StudentSelect
type StudentDetail = StudentGetPayload<{
select: typeof studentDetailSelect
}>
// ✅ StudentDetail includes: { id: string, firstName: string, age: number }// Only include virtual fields when selected
const basicSelect = {
id: true,
firstName: true,
// age NOT selected
} satisfies StudentSelect
type BasicStudent = StudentGetPayload<{
select: typeof basicSelect
}>
// ✅ BasicStudent includes: { id: string, firstName: string }
// age is NOT included because it wasn't selectedconst student = await context.db.student.findUnique({
where: { id: '1' },
select: studentDetailSelect,
})
if (student) {
console.log(student.id) // ✅ Typed
console.log(student.firstName) // ✅ Typed
console.log(student.age) // ✅ Typed - virtual field!
}- Type-Safe Selection: Virtual fields are included in
{ListName}Selecttypes - Conditional Inclusion: Only selected virtual fields appear in result types
- Select & Include Support: Works with both
selectandincludemodes - Transformed Fields: Also handles fields with
resultExtensiontransformations - Full Documentation: Generated types include comprehensive JSDoc comments
- Backward Compatible: Doesn't break existing code
The solution leverages TypeScript's conditional types to:
- Detect if
Thas aselectorincludeproperty - Check if each virtual field is selected (
T['select'][K] extends true) - Use mapped types to include only selected virtual fields
- Merge with Prisma's base
GetPayloadtype
This creates a type that mirrors runtime behavior exactly - virtual fields only appear when they're selected.
Run tests with:
cd packages/cli
pnpm testThe test suite includes:
- Unit tests for
generateGetPayloadType() - Integration tests with full config examples
- Snapshot tests for generated output
- Tests for both virtual and transformed fields
packages/cli/src/generator/types.ts- AddedgenerateGetPayloadType()functionpackages/cli/src/generator/types.test.ts- Added virtual field testspackages/cli/src/generator/__snapshots__/types.test.ts.snap- Generated snapshots
No migration needed! This is a pure type enhancement. Existing code continues to work, and users can adopt the new types incrementally:
- Replace
Prisma.{ListName}Selectwith{ListName}Selectfrom.opensaas/types - Replace
Prisma.{ListName}GetPayload<T>with{ListName}GetPayload<T>from.opensaas/types - Regenerate types with
pnpm generate
Potential future improvements:
- Generate
{ListName}Argstypes that wrap all Prisma args with virtual field support - Module augmentation to extend
Prismanamespace directly (optional) - Documentation examples in official docs