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

如何使用动态键调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 06:12:03