You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Prisma中为嵌套Include返回正确的TypeScript类型?

Prisma嵌套Include参数化后类型丢失问题解决

问题背景

数据模型定义如下:

model Foo {
  id String @id @default(cuid())
  bar Bar[]
}

model Bar {
  id String @id @default(cuid())
  foo Foo @relation(fields: [fooId], references: [id], onDelete: Cascade, onUpdate: Cascade)
  fooId String
  baz Baz[]
}

model Baz {
  id String @id @default(cuid())
  bar Bar @relation(fields: [barId], references: [id], onDelete: Cascade, onUpdate: Cascade)
  barId String
}

当直接在函数内写死include配置时,TypeScript能正确推导返回类型,识别嵌套的baz字段:

async function getFooById(id: Foo['id']) {
  return prisma.foo.findUnique({
    where: { id },
    include: { bar: { include: { baz: true } } },
  })
}

const foo = await getFooById('123')

if (foo !== null) {
  const baz = foo.bar[0].baz // 类型正常识别
}

但将include参数化后,类型推导失效,无法识别foo.bar[0].baz:

import type { DefaultArgs } from '@prisma/client/runtime/library.js'

async function getFooById(
  id: Foo['id'],
  { include }: { include: Prisma.FooInclude<DefaultArgs> } = { include: {} },
) {
  return prisma.foo.findUnique({
    where: { id },
    include,
  })
}

const foo = await getFooById('123', { include: { bar: { include: { baz: true } } } })

if (foo !== null) {
  // 错误:baz不存在于foo.bar[0]类型上
  const baz = foo.bar[0].baz
}

尝试泛型写法也未解决:

async function getFooById<ExtArgs extends Types.Extensions.InternalArgs = Types.Extensions.DefaultArgs>(
  id: Foo['id'],
  { include }: { include: Prisma.FooInclude<ExtArgs> } = { include: {} },
) {
  // …
}

使用版本:Prisma 5.8.0,TypeScript 5.3.3

解决方案

要让TypeScript正确推导参数化include后的返回类型,需要利用Prisma提供的泛型类型工具,将include参数的类型与函数返回类型绑定:

正确的泛型写法

import type { Prisma, Foo } from '@prisma/client'

async function getFooById<T extends Prisma.FooInclude>(
  id: Foo['id'],
  { include }: { include?: T } = {}
): Promise<Prisma.FooGetPayload<{ include: T }> | null> {
  return prisma.foo.findUnique({
    where: { id },
    include,
  })
}

使用示例

const foo = await getFooById('123', { include: { bar: { include: { baz: true } } } })

if (foo !== null) {
  const baz = foo.bar[0].baz // 类型正确识别,无报错
}

原理说明

  • T extends Prisma.FooInclude:约束泛型T为Foo模型允许的include配置类型
  • Prisma.FooGetPayload<{ include: T }>:Prisma提供的类型工具,根据传入的include配置,自动推导对应的返回数据类型
  • 显式指定返回类型为Promise<... | null>:匹配findUnique可能返回null的特性

这样就能让TypeScript根据传入的include参数,动态推导函数返回的Foo对象的完整类型,包括所有嵌套关联字段。


内容的提问来源于stack exchange,提问作者Thomas Luzat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 14:02:43