TypeScript如何实现支持$后缀可选Observable的自定义映射类型
实现方案
你只需要新增一个常用的 UnionToIntersection 工具类型,把你现有逻辑生成的所有二选一对象联合转换成交叉类型,即可去掉嵌套层级,完整实现如下:
import type { Observable } from 'rxjs' // TS 通用工具类型:将联合类型转换为交叉类型 type UnionToIntersection<U> = (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never type OptionalObservables<T> = UnionToIntersection< { [K in keyof T]: K extends string ? { [P in K]: T[K] } | { [P in `${K}$`]: Observable<T[K]> } : never }[keyof T] // 提取所有属性值的联合 >
效果验证
用你的示例类型测试:
type Animal = { species: string, noise: string } type AnimalWithOptionalObservables = OptionalObservables<Animal>
最终推导出来的类型和你期望的完全一致:
({ species: string } | { species$: Observable<string> }) & ({ noise: string } | { noise$: Observable<string> })
合法/非法用例校验
import { of } from 'rxjs' // 合法:全原始键 const a1: AnimalWithOptionalObservables = { species: 'dog', noise: 'bark' } // 合法:混合原始键和$后缀键 const a2: AnimalWithOptionalObservables = { species: 'dog', noise$: of('bark') } // 合法:全$后缀键 const a3: AnimalWithOptionalObservables = { species$: of('dog'), noise$: of('bark') } // 非法:同一个属性同时存在原始键和$后缀键 const a4: AnimalWithOptionalObservables = { species: 'dog', species$: of('dog'), noise: 'bark' } // 非法:缺少属性 const a5: AnimalWithOptionalObservables = { species: 'dog' }
替代方案(运行时校验)
如果需要更友好的类型提示,也可以用工厂函数封装类型约束,使用体验一致:
function defineObservableObject<T>(input: OptionalObservables<T>) { return input } // 用法 const animal = defineObservableObject<Animal>({ species: 'dog', noise$: of('bark') })
内容的提问来源于stack exchange,提问作者Simon_Weaver
相关产品推荐
相关产品推荐

