如何使用动态键调用Prisma资源?解决findMany类型兼容错误
Prisma通用findMany调用类型不兼容问题解决
问题背景
尝试实现一个通用方法从Prisma获取任意模型的数据,但调用findMany()时触发TypeScript类型错误,错误提示联合类型的函数签名无法兼容。
原代码
import { Prisma, PrismaClient } from '@prisma/client'; import { NextApiRequest, NextApiResponse } from 'next'; const prisma = new PrismaClient(); const getAllOfResource = async (resourceName: Prisma.ModelName) => { const resource = prisma[resourceName as Prisma.ModelName]; if (!resource.findMany) throw Error('Error: findMany not found, does resource exist?'); return await resource.findMany(); }; const validateResourceTypeExists = (resourceType: unknown): resourceType is Prisma.ModelName => { return typeof resourceType === 'string' && Object.keys(Prisma.ModelName).includes(resourceType); }; export default async function handler(req: NextApiRequest, res: NextApiResponse) { const requestedResource = req.query.id; if (!validateResourceTypeExists(requestedResource)) { res.status(500).json({ message: 'Please use a valid resource type', }); } else { const resource = await getAllOfResource(requestedResource); return res.status(200).json(resource); } }
错误信息
This expression is not callable. Each member of the union type '(<T extends mediaFindManyArgs<DefaultArgs>>(args?: SelectSubset<T, mediaFindManyArgs<DefaultArgs>> | undefined) => PrismaPromise<...>) | (<T extends booksFindManyArgs<...>>(args?: SelectSubset<...> | undefined) => PrismaPromise<...>)' has signatures, but none of those signatures are compatible with each other
疑问:所有Prisma.ModelName对应的模型都应该有findMany方法,为何会出现类型不兼容问题?
问题原因
TypeScript对联合类型的函数调用有严格的兼容性要求:只有当联合类型中所有函数的签名完全兼容时,才能直接调用。而Prisma每个模型的findMany方法参数类型(如mediaFindManyArgs、booksFindManyArgs)是各自生成的专属类型,属于不同的签名,因此TypeScript认为无法安全调用。
解决方案
方案一:类型断言简化调用
直接将模型实例断言为带有通用findMany方法的类型,绕开联合类型的签名检查:
const getAllOfResource = async (resourceName: Prisma.ModelName) => { // 断言为具备通用findMany方法的类型 const resource = prisma[resourceName] as { findMany: () => Promise<unknown[]> }; return await resource.findMany(); };
如果需要支持传递查询参数,可以扩展泛型类型:
type GenericPrismaModel = { findMany<T = unknown>(args?: Record<string, any>): Promise<T[]>; }; const getAllOfResource = async <T = unknown>(resourceName: Prisma.ModelName) => { const resource = prisma[resourceName] as GenericPrismaModel; return await resource.findMany<T>(); };
方案二:利用Prisma泛型工具类型约束
通过类型别名过滤出所有带findMany方法的模型,让TypeScript明确类型兼容性:
import type { Prisma, PrismaClient } from '@prisma/client'; // 仅保留拥有findMany方法的模型名称 type ModelWithFindMany = { [K in Prisma.ModelName]: PrismaClient[K] extends { findMany: any } ? K : never; }[Prisma.ModelName]; const getAllOfResource = async (resourceName: ModelWithFindMany) => { const resource = prisma[resourceName]; // 此时TypeScript可确定findMany方法签名兼容 return await resource.findMany(); };
方案三:精准返回类型优化
如果需要让返回类型更精准,结合Prisma的Result工具类型实现类型推断:
import type { Prisma, PrismaClient } from '@prisma/client'; const getAllOfResource = async <T extends Prisma.ModelName>(resourceName: T) => { const resource = prisma[resourceName]; // 利用Prisma.Result推断findMany的返回类型 return await resource.findMany() as Prisma.Result<PrismaClient[T], {}, 'findMany'>; };
调用该函数时,TypeScript会根据传入的模型名自动推断返回的数据类型。
内容的提问来源于stack exchange,提问作者morthemex
相关产品推荐
相关产品推荐

