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

Typescript实现嵌套对象带对应类型的深度keyof,适配MongoDB点表示法补全

实现方案

以下是完整的类型实现,完全覆盖你提出的所有需求场景:

// 基础工具:判断两个类型是否完全相等
type Equal<X, Y> =
  (<T>() => T extends X ? 1 : 2) extends
  (<T>() => T extends Y ? 1 : 2) ? true : false;

// 提取对象中非readonly修饰的属性键
type WritableKeys<T> = {
  [K in keyof T]-?: Equal<{ [P in K]: T[K] }, { -readonly [P in K]: T[K] }> extends true ? K : never
}[keyof T];

// 路径拼接工具,生成点表示法的键
type JoinPath<P extends string, K extends string | number> =
  P extends '' ? `${K}` : `${P}.${K}`;

// 递归生成点表示法的键值对集合
type DotNotationEntries<T, StopTypes = Date | RegExp | boolean | string | number | null | undefined, P extends string = ''> =
  // 命中终止类型直接返回空,不展开内部属性
  T extends StopTypes ? {} :
  // 处理数组类型
  T extends Array<infer U> ? 
    DotNotationEntries<U, StopTypes, JoinPath<P, number>> :
  // 处理元组类型
  T extends [any, ...any[]] ? {
    [K in keyof T as JoinPath<P, K & (string | number)>]: T[K]
  } :
  // 处理普通对象类型
  T extends object ? {
    [K in WritableKeys<T> as JoinPath<P, K & string>]: T[K]
  } & {
    [K in WritableKeys<T> as keyof DotNotationEntries<T[K], StopTypes, JoinPath<P, K & string>>]: 
      DotNotationEntries<T[K], StopTypes, JoinPath<P, K & string>>[keyof DotNotationEntries<T[K], StopTypes, JoinPath<P, K & string>>]
  } : {};

// 对外暴露的最终工具类型
type MongoFieldType<T, StopTypes = Date | RegExp | boolean | string | number | null | undefined> =
  // 保留原对象的可写属性
  { [K in WritableKeys<T>]: T[K] }
  // 合并点表示法的键值对,过滤掉和原属性重复的根级键
  & Omit<DotNotationEntries<T, StopTypes>, keyof T>;

使用示例验证

基础嵌套对象场景

interface IPerson {
  name: string;
  age: number;
  contact: {
    address: string;
    visitDate: Date;
  }
}
type TPerson = MongoFieldType<IPerson>;
// 生成结果:
// {
//   name: string;
//   age: number;
//   contact: { address: string; visitDate: Date; };
//   "contact.address": string;
//   "contact.visitDate": Date;
// }

数组索引签名场景

interface IPerson1 {
  contact: {
    address: string;
    visitDate: Date;
  }[]
}
type TPerson1 = MongoFieldType<IPerson1>;
// 生成结果:
// {
//   contact: { address: string; visitDate: Date; }[];
//   [x: `contact.${number}.address`]: string;
//   [x: `contact.${number}.visitDate`]: Date;
// }

元组场景

interface IPerson2 {
  contact: [string, Date]
}
type TPerson2 = MongoFieldType<IPerson2>;
// 生成结果:
// {
//   contact: [string, Date];
//   "contact.0": string;
//   "contact.1": Date;
// }

Readonly属性过滤场景

interface IPerson3 {
  readonly _id: string;
  age: number;
  readonly _created_date: Date;
}
type TPerson3 = MongoFieldType<IPerson3>;
// 生成结果:
// {
//   age: number;
// }

可选属性保留场景

interface IPerson4 {
  contact: {
    address: string;
    visitDate?: Date;
  }[];        
}
type TPerson4 = MongoFieldType<IPerson4>;
// 生成结果:
// {
//   contact: { address: string; visitDate?: Date; }[];
//   [x: `contact.${number}.address`]: string;
//   [x: `contact.${number}.visitDate`]?: Date;
// }

交叉类型场景

interface IPerson5 {
  contact: { address: string; } & { visitDate: Date; }
}
type TPerson5 = MongoFieldType<IPerson5>;
// 生成结果:
// {
//   contact: { address: string; } & { visitDate: Date; };
//   "contact.address": string;
//   "contact.visitDate": Date;
// }

自定义终止类型场景

默认终止类型已经包含Date、string、number等常用基础类型,无需额外配置即可避免展开内置类型的内部属性,也可以手动传入第二个泛型参数自定义终止类型:

// 示例:新增Buffer为终止类型
type CustomStopTypes = Date | RegExp | boolean | string | number | null | undefined | Buffer;
type TPersonCustom = MongoFieldType<IPerson, CustomStopTypes>;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 03:48:01