如何实现Redux Toolkit式builder模式构建无类型擦除的强类型Record
解法
要实现完全无类型擦除的builder模式强类型Map构建,核心是利用TypeScript的泛型交叉累积能力,配合satisfies关键字保留原始record类型,以下是无冗余、无类依赖的标准函数式实现:
完整可运行代码
// 1. 基础类型定义 type BaseRecord<Name extends string = string> = { name: Name } & Record<string, string | number> // 2. Builder类型,泛型T存储当前已累积的RecordMap类型 type RecordMapBuilder<T extends Record<string, BaseRecord> = {}> = { addRecord: <R extends BaseRecord>(record: R) => RecordMapBuilder<T & Record<R['name'], R>> } // 3. 核心构造函数实现 function createRecordMap<T extends Record<string, BaseRecord>>( builderCallback: (builder: RecordMapBuilder<{}>) => RecordMapBuilder<T> ): T { const recordMap: Record<string, BaseRecord> = {} const builder: RecordMapBuilder = { addRecord(record) { recordMap[record.name] = record return builder } } // 执行构造逻辑,运行时结构和类型完全对齐 builderCallback(builder) return recordMap as T }
使用示例
// 注意:用satisfies而非显式标注BaseRecord,避免类型擦除 const recordA = { name: "recordA", fooString: "foo", fooNumber: 6 } satisfies BaseRecord const recordB = { name: "recordB", barString: "bar", barNumber: 2, barSecondString: "bar2" } satisfies BaseRecord // 链式调用构建Map const newRecordMap = createRecordMap(builder => builder.addRecord(recordA).addRecord(recordB) )
类型校验结果
newRecordMap.recordA.fooString类型自动推断为stringnewRecordMap.recordB.barSecondString类型自动推断为string- 访问不存在的key(如
newRecordMap.nonExist)会直接触发TS类型报错,完全符合预期。
原代码问题说明
- 之前的泛型设计错误:将
createRecordMap的泛型绑定到builder实例本身,TS无法从回调的副作用调用中反向推断累积类型,改为从builder返回值的泛型中提取最终Map类型即可解决。 - 显式标注
recordA: BaseRecord会拓宽类型,使用satisfies关键字可以在校验类型合法性的同时保留原始字面量类型。 - 唯一的类型断言
recordMap as T是完全安全的,运行时结构和泛型定义100%对齐。
内容的提问来源于stack exchange,提问作者sam256
相关产品推荐
相关产品推荐

