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

如何实现支持嵌套与点语法的MongoDB ORM TypeScript过滤类型

TypeScript 实现支持嵌套与点语法的MongoDB过滤类型约束

给定如下TypeScript模型类型:

type MyModel = {
  string: string
  subObject: {
    subObjString: string
  }
}

需要实现一个过滤类型,满足以下输入约束:

  • ✅ { string: 'foo' }(合法)
  • ✅ { subObject: { subObjString: 'foo'} }(合法)
  • ✅ { 'subObject.subObjString': 'foo' }(合法)
  • ❌ { 'subObject.notExistingprop': 'foo' }(非法)
  • ❌ { notExisting: 'foo' }(非法)

目标是为MongoDB ORM的过滤输入做类型约束,同时支持传统嵌套对象语法和点分隔的路径语法。用户尝试的FilterType实现存在问题,推导结果为unknown,无法满足需求:

type FilterType<O extends Record<string, any>> = {
    [K in (keyof O & string)]: O[K] extends Record<string, any> ? K extends symbol ? never : `${K}.${FilterType<O>}` : never
}[string]

type FilterTypeForModel = FilterType<MyModel> // 被解析为unknown

const test1 = ['string', 'subObject'] satisfies FilterTypeForModel // 应合法
const test2 = ['subObject.subObjString'] satisfies FilterTypeForModel // 应合法
const test3 = ['subObject.notExistingProp'] satisfies FilterTypeForModel // 应非法

解决方案

我们需要分两步实现:先递归生成所有合法的点路径字符串,再将点路径与嵌套对象结构合并,最终得到符合要求的过滤类型。

1. 生成所有合法的点路径

实现递归类型PathKeys,遍历对象所有层级生成点分隔路径:

type PathKeys<T extends Record<string, any>> = {
  [K in keyof T & string]: 
    T[K] extends Record<string, any> 
      ? K | `${K}.${PathKeys<T[K]>}` 
      : K
}[keyof T & string]

该类型会递归处理每个键:若当前键对应值为对象,则同时保留当前键和当前键+子对象路径的组合;若为非对象类型,则仅保留当前键。

2. 合并点路径与嵌套对象结构

实现FilterType,同时支持点路径键和嵌套对象两种语法:

type FilterType<T extends Record<string, any>> = 
  // 支持点路径作为键的对象结构
  Record<PathKeys<T>, any> & 
  // 支持传统嵌套对象结构
  {
    [K in keyof T & string]?: 
      T[K] extends Record<string, any> 
        ? FilterType<T[K]> 
        : any
  }

通过交叉类型合并两种结构,既允许用点路径直接作为键,也支持嵌套的对象格式。


验证测试用例

用MyModel验证约束效果:

type FilterTypeForModel = FilterType<MyModel>

// 合法输入(无TypeScript错误)
const valid1: FilterTypeForModel = { string: 'foo' }
const valid2: FilterTypeForModel = { subObject: { subObjString: 'foo'} }
const valid3: FilterTypeForModel = { 'subObject.subObjString': 'foo' }

// 非法输入(TypeScript会报错提示)
const invalid1: FilterTypeForModel = { 'subObject.notExistingprop': 'foo' }
const invalid2: FilterTypeForModel = { notExisting: 'foo' }

若要验证数组形式的路径列表,直接使用PathKeys<MyModel>即可:

const test1 = ['string', 'subObject'] satisfies PathKeys<MyModel>[] // 合法
const test2 = ['subObject.subObjString'] satisfies PathKeys<MyModel>[] // 合法
const test3 = ['subObject.notExistingProp'] satisfies PathKeys<MyModel>[] // 非法,TypeScript报错

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 04:52:36