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

如何定义部分类型化对象?保留IntelliSense与类型检查支持

如何定义部分类型明确、其余为any的对象并保留IntelliSense支持?

问题描述

需求是定义一个对象类型,其中部分属性类型明确,其余属性本质为any,核心目标是为已定义的部分提供IntelliSense支持,同时最好附带类型检查能力。

现有辅助类型用于将元组转换为交叉类型:

type TupleToIntersection<T extends any[]> = {
  [I in keyof T]: (x: T[I]) => void
}[number] extends (x: infer U) => void
  ? U
  : never

当前实现的Collection类型在混入any时会丢失所有类型信息:

type A = {
  A: { name: "Foo" }
}
type B = {
  B: { name: "Bar" }
}
    
type Collection<T extends any[]> = TupleToIntersection<{ [I in keyof T]: T[I] }>

declare const C: Collection<[A, B]>
C.A.name // 正常获得"Foo"的类型提示和检查

declare const D: Collection<[A, any]>
D // 整个类型变为any,丢失IntelliSense
D.A.name // 语法合法但无类型提示

尝试过用类型守卫强制转换,但需要额外代码,不够便捷,希望找到更简洁的类型定义方案。

解决方案

核心思路是避免any与已知类型直接交叉(交叉any会导致整个类型变为any),而是先过滤元组中的any类型,保留已知类型的交叉结果,再与允许任意属性的类型合并。

步骤1:定义过滤元组中any的工具类型

type FilterOutAny<T extends any[]> = T extends [infer F, ...infer R]
  ? // 判断当前元素是否为any,是则跳过,否则保留并递归处理剩余元素
    F extends any
    ? FilterOutAny<R>
    : [F, ...FilterOutAny<R>]
  : [];

步骤2:重新定义Collection类型

type Collection<T extends any[]> = 
  // 先过滤元组中的any,再交叉保留的已知类型
  TupleToIntersection<FilterOutAny<T>> & 
  // 合并任意属性的类型,允许其余属性为any
  Record<string, any>;

测试验证

declare const D: Collection<[A, any]>;
D.A.name; // 正常获得"Foo"的IntelliSense和类型检查
D.randomKey; // 类型为any,无报错
D.randomKey = 123; // 合法
D.randomKey = "test"; // 合法

declare const E: Collection<[A, B, any]>;
E.A.name; // 类型为"Foo"
E.B.name; // 类型为"Bar"
E.otherProp; // 类型为any

原理说明

  • FilterOutAny会剔除元组中所有any类型的元素,只保留明确的类型定义;
  • 对保留的类型执行交叉操作,确保已知属性的类型被正确合并;
  • 最后与Record<string, any>交叉,允许对象拥有任意额外属性,且这些属性的类型为any;
  • 这种方式既保留了已知属性的IntelliSense和类型检查能力,又满足了其余属性为any的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 01:20:01