如何定义部分类型化对象?保留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
相关产品推荐
相关产品推荐

