TypeScript实现MongoDB风格数组Unwind泛型函数的类型定义问题
实现带类型推导的MongoDB风格unwind函数(TypeScript)
先看修正后的完整可运行代码,再拆解解决你的两个核心问题:
describe('unwindByKey', () => { // 解决问题1和2的类型定义:移除指定数组键,同时将该键的类型转为数组元素类型 type UnwindItemType<T, K extends keyof T> = Omit<T, K> & { [P in K]: T[K] extends (infer U)[] ? U : T[K] } // 修正后的函数:约束传入的key必须对应对象中的数组属性,返回类型自动推导 function unwindByKey<T extends {}, K extends keyof T & (T[K] extends any[] ? K : never)>( items: T[], key: K ): UnwindItemType<T, K>[] { return items.reduce((accum, item) => { const { [key]: keyItems, ...itemRest } = item; const unwoundItems = keyItems.map((keyItem) => ({ ...itemRest, [key]: keyItem })) as UnwindItemType<T, K>[]; return [...accum, ...unwoundItems]; }, [] as UnwindItemType<T, K>[]); } // 示例测试 interface DataItem { a: number; b: number[]; } // 现在UnwoundDataItem会自动推导为 {a: number; b: number} type UnwoundDataItem = UnwindItemType<DataItem, 'b'>; const data: DataItem[] = [ { a: 0, b: [0, 1, 2] }, { a: 1, b: [3, 4, 5] }, ]; const expectedData: UnwoundDataItem[] = [ { a: 0, b: 0 }, { a: 0, b: 1 }, { a: 0, b: 2 }, { a: 1, b: 3 }, { a: 1, b: 4 }, { a: 1, b: 5 }, ]; test('it works', () => { const result = unwindByKey(data, 'b'); expect(result.length).toBe(expectedData.length); expect(result).toEqual(expectedData); // 此时result的类型会自动匹配UnwoundDataItem[],类型完全正确 }); });
核心问题拆解解决
从输入泛型确定展开后项的类型
使用TypeScript的infer关键字提取数组元素类型:T[K] extends (infer U)[] ? U : T[K]。这段逻辑会检查T[K]是否为数组类型,若是则提取内部元素类型U,最终将原数组键的类型替换为单个元素类型。移除包含嵌套数组的键并替换类型
- 先用
Omit<T, K>移除原对象中指定的数组键,保留其余属性。 - 再通过映射类型
{ [P in K]: ... }重新定义该键的类型为数组元素类型。 - 最后用
&运算符将两部分类型合并,得到展开后的完整对象类型。
- 先用
额外优化:类型安全约束
函数给泛型K加了约束:K extends keyof T & (T[K] extends any[] ? K : never)。这样能确保调用时传入的key必须是对象中值为数组的属性,避免传入非数组键导致的运行时错误,同时增强IDE的类型提示。
内容的提问来源于stack exchange,提问作者cyrf
相关产品推荐
相关产品推荐

