如何为数组+reduce函数的动态字典正确添加类型?
为动态添加的Reducers实现类型安全的字典
问题分析
你当前的reducers字段使用Record<string, Reducer<unknown, unknown>>虽然允许动态添加,但丢失了每个key对应Reducer的具体类型信息,也无法在编译时自动推断后续操作的类型。我们需要一种既能支持动态添加,又能保证单个Reducer内部T和U一致性,同时跟踪整体结构类型的方案。
解决方案:泛型类跟踪Reducers结构
通过让Part类接受泛型参数来跟踪reducers的具体结构,每次添加Reducer时自动细化类型:
// 修正原类型中的拼写错误:initalValue -> initialValue type ReduceFn<T, U> = (result: U, nextValue: T, index: number) => U; type Reducer<T, U> = { array: T[]; reducefn: ReduceFn<T, U>; initialValue: U }; class Part<R extends Record<string, Reducer<any, any>> = {}> { indices: number[]; reducers: R; constructor(indices: number[]) { this.indices = indices; this.reducers = {} as R; } addReducer<K extends string, T, U>( key: K, reducer: Reducer<T, U> ): Part<R & Record<K, Reducer<T, U>>> { // 合并原有reducers与新reducer,并断言为更新后的类型 this.reducers = { ...this.reducers, [key]: reducer } as R & Record<K, Reducer<T, U>>; return this as Part<R & Record<K, Reducer<T, U>>>; } // 示例:获取归约后的值(子集化逻辑可自行补充) getReducedValue<K extends keyof R>(key: K): ReturnType<R[K]['reducefn']> { const { array, reducefn, initialValue } = this.reducers[key]; // 此处加入你已实现的数组子集化逻辑,比如: // const subset = array.filter((_, idx) => this.indices.includes(idx)); return array.reduce(reducefn, initialValue); } }
使用示例
// 初始化实例,reducers为空 const part = new Part([0, 2]); // 添加第一个Reducer,实例类型自动更新 const partWithSum = part.addReducer('sum', { array: [1, 2, 3, 4], reducefn: (acc, val) => acc + val, initialValue: 0 }); // 添加第二个Reducer,类型继续细化 const partWithSumAndJoin = partWithSum.addReducer('join', { array: ['a', 'b', 'c', 'd'], reducefn: (acc, val) => acc + val, initialValue: '' }); // 自动推断返回值类型 const sum = partWithSumAndJoin.getReducedValue('sum'); // sum: number const joined = partWithSumAndJoin.getReducedValue('join'); // joined: string // 编译时错误:访问不存在的key partWithSumAndJoin.getReducedValue('invalid'); // 类型提示:"invalid"不在"sum" | "join"中
简化方案(牺牲部分类型安全)
如果不需要跟踪每个key的具体类型,仅需保证单个Reducer内部类型一致,可以保留原结构但优化addReducer的泛型约束:
type ReduceFn<T, U> = (result: U, nextValue: T, index: number) => U; type Reducer<T, U> = { array: T[]; reducefn: ReduceFn<T, U>; initialValue: U }; class Part { indices: number[]; reducers: Record<string, Reducer<unknown, unknown>>; constructor(indices: number[]) { this.indices = indices; this.reducers = {}; } addReducer<T, U>(key: string, reducer: Reducer<T, U>) { this.reducers[key] = reducer; return this; } // 获取值时需手动指定类型或断言 getReducedValue<T, U>(key: string): U { const reducer = this.reducers[key] as Reducer<T, U>; const subset = reducer.array.filter((_, idx) => this.indices.includes(idx)); return subset.reduce(reducer.reducefn, reducer.initialValue); } }
这种方案实现简单,但调用getReducedValue时需要手动传入类型,存在误写风险,适合对类型安全要求不高的场景。
内容的提问来源于stack exchange,提问作者Adam B.
相关产品推荐
相关产品推荐

