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

如何用TypeScript递归生成Prisma风格扁平化泛型筛选键

实现扁平化Prisma筛选条件的TypeScript类型定义

需求背景

Prisma支持嵌套结构的筛选条件,示例如下:

prisma.user.findMany({
     where: {
         name: { contains: 'jeff' },
         city: {
             name: { contains: 'hol' },
             state: { 
                 acr: {
                     equals: 'vegas'
                 }
             }
         }
     }
})

给定如下对象结构:

{
    name: '',
    city: {
        name: '',
        state: {
           acr: ''
        }
    }
}

需要生成扁平化的筛选条件类型,而非嵌套结构:

type UserFilters = {
    name_contains?: string,
    name_equals?: string,
    city_name_contains?: string
    city_name_equals?: string
    city_state_acr_contains?: string
    city_state_acr_equals?: string
}

当前实现的问题

原类型定义会生成嵌套结构,不符合需求:

type FilterR<Form> = Form extends object
    ? Partial<{
            [Key in string & keyof Form as `${Key}_${'contains' | 'equals'}`]: FilterR<Form[Key]>
      }>
    : Form

生成的嵌套结果示例:

Partial<{
    name_contains: "";
    name_equals: "";
    city_contains: Partial<{
        name_contains: "";
        name_equals: "";
        state_contains: Partial<{
            acr_contains: "";
            acr_equals: "";
        }>;
        state_equals: Partial<{
            acr_contains: "";
            acr_equals: "";
        }>;
    }>;
    city_equals: Partial<...>;
}>

解决方案

通过递归遍历对象层级,仅在叶子节点(基础类型)生成筛选后缀,中间节点负责拼接路径,最终合并为扁平化对象:

// 辅助类型:递归生成所有扁平化的筛选键值对
type FlattenFilter<T, Path extends string = ""> = 
  T extends object 
    ? { [K in keyof T & string]: FlattenFilter<T[K], `${Path}${Path extends "" ? "" : "_"}${K}`> }[keyof T & string]
    : { [Op in "contains" | "equals"]: { [Key in `${Path}_${Op}`]: T } }[keyof "contains" | "equals"];

// 将联合类型的键值对合并为单个对象
type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer R) => void ? R : never;

// 最终的筛选类型,所有属性可选
type FilterR<T> = Partial<UnionToIntersection<FlattenFilter<T>>>;

用法示例

// 定义目标对象结构
const userSchema = {
  name: '',
  city: {
    name: '',
    state: {
      acr: ''
    }
  }
};

// 生成扁平化筛选类型
type UserFilters = FilterR<typeof userSchema>;

生成的UserFilters类型与期望完全一致,所有筛选键均为下划线拼接的扁平化格式,属性均为可选。

实现逻辑说明

  1. FlattenFilter:递归遍历对象的每个层级,遇到对象类型时继续拼接路径,遇到基础类型时生成路径_contains和路径_equals两个键值对
  2. UnionToIntersection:将递归生成的多个键值对联合类型,转换为交叉类型以合并成单一对象
  3. Partial:将所有属性设为可选,符合Prisma筛选条件的可选特性

内容的提问来源于stack exchange,提问作者Lucas Steffen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 03:21:05