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

TurboRepo中使用Prisma方法时TypeScript导出类型不完整问题

TurboRepo + NestJS + Prisma 跨包类型推断失效解决方案

问题场景

在TurboRepo monorepo架构中,NestJS服务端包通过export.d.ts导出API相关类型时,前端包无法完整推断使用Prisma方法返回的类型,仅服务端包内部类型推断正常。具体表现为:

  • 当服务端方法直接返回字面量对象时,前端包能正确推断类型
  • 当服务端方法返回Prisma查询结果时,前端包中该类型被推断为Promise<any>

服务端核心代码示例:

// export.d.ts
import { AuthController } from './auth.controller';
import { SignInDto } from './auth.dto';

export * from './auth.controller';

type Wrapper<T extends (...args: any) => any> = Awaited<ReturnType<T>>;

export namespace ApiTypes {
  export interface SignInPayload extends SignInDto {}
  export interface SignInResponse extends Wrapper<typeof AuthController.prototype.signin> { token: string }
}
// auth.service.ts
@Injectable()
export class AuthService {
  constructor(private prisma: PrismaClient) {}

  async signin(payload: SignInDto) {
    return await this.prisma.staffUsers.findUnique({
      where: { username: payload.identifier },
    });
  }
}

原因分析

Prisma Client的返回类型是动态生成的内部类型,跨包传递时,前端包无法解析服务端包依赖的Prisma类型定义,导致TypeScript无法正确推断类型,最终退化为any。而直接返回字面量对象时,类型是显式的基础类型/自定义类型,能被跨包正确识别。

可行解决方案

方案1:显式指定服务层返回类型为Prisma模型类型

在服务层方法中,直接指定返回类型为Prisma自动生成的模型类型,让export.d.ts中的类型包装器能捕获到明确的类型:

// auth.service.ts
import { Injectable } from '@nestjs/common';
import { PrismaClient, StaffUsers } from '@prisma/client'; // 导入Prisma模型类型
import { SignInDto } from './auth.dto';

@Injectable()
export class AuthService {
  constructor(private prisma: PrismaClient) {}

  // 显式指定返回类型为Prisma生成的StaffUsers类型
  async signin(payload: SignInDto): Promise<StaffUsers> {
    return await this.prisma.staffUsers.findUnique({
      where: { username: payload.identifier },
    });
  }
}

此方案利用Prisma自动生成的模型类型,无需手动维护复杂类型,当Prisma Schema更新时,类型会自动同步。

方案2:在export.d.ts中直接导入并复用Prisma模型类型

跳过从控制器方法推断类型的步骤,直接在export.d.ts中导入Prisma模型类型并组合:

// export.d.ts
import { SignInDto } from './auth.dto';
import type { StaffUsers } from '@prisma/client'; // 导入Prisma模型类型

export namespace ApiTypes {
  export interface SignInPayload extends SignInDto {}
  // 直接复用StaffUsers类型并扩展token字段
  export interface SignInResponse extends StaffUsers { token: string }
}

此方案更直接,避免了依赖控制器方法的隐式类型推断,跨包类型传递更可靠。

方案3:优化服务端包TypeScript配置确保类型完整输出

调整服务端包的tsconfig.json,确保类型声明文件能完整包含Prisma相关类型:

{
  "compilerOptions": {
    "declaration": true, // 生成类型声明文件
    "declarationMap": true, // 生成类型映射,方便调试
    "emitDeclarationOnly": false, // 确保类型和编译代码同时输出
    "include": [
      "src/**/*",
      "../prisma/**/*" // 包含Prisma生成的类型文件路径
    ]
  }
}

同时确保TurboRepo的turbo.json中,服务端包的构建任务能正确输出类型声明文件,前端包能正确依赖服务端包的类型输出。

总结

优先推荐方案1或方案2,既利用Prisma自动类型生成的优势,又能解决跨包类型推断失效的问题,维护成本极低。

内容的提问来源于stack exchange,提问作者KAVYA SHARMA

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 09:17:04