如何根据值类型限制泛型Key?TypeScript groupBy类型约束问题
TypeScript 泛型约束问题:限制groupBy的键对应值为string/number类型
我需要实现一个groupBy函数,要求泛型Key对应的Item[Key]必须是string或number类型,但当前两种实现都存在问题:
第一种实现的错误
在Record<Item[Key], Item>处报错:Type does not satisfy the constraint string | number | symbol,代码如下:
function groupBy< Item, Key extends keyof { [T in keyof Item]: Item[T] extends PropertyKey ? Item[T] : never} >(items: Item[], key: Key): Record<Item[Key], Item>{ const groups = {} as Record<Item[Key], Item>; for(const item of items){ groups[item[key]] = { ...groups[item[key]], ...item }; } return groups; }
第二种实现的错误
调用时传入'name'作为第二个参数会报错:Object literal may only specify known properties, and 'id' does not exist in type 'Record<"name", PropertyKey>',代码如下:
function groupBy< Item extends Record<Key, PropertyKey>, Key extends keyof Item >(items: Item[], key: Key): Record<Item[Key], Item>{ const groups = {} as Record<Item[Key], Item>; for(const item of items){ groups[item[key]] = { ...groups[item[key]], ...item }; } return groups; }
预期效果
调用groupBy时,传入'isAlive'应触发类型错误,传入'id'、'name'、'age'应正常运行:
groupBy([ {id: 1, name: 'Marc', isAlive: false }, {id: 1, age: 21 }, ], 'isAlive'); // 应该触发类型错误 groupBy([ {id: 1, name: 'Marc', isAlive: false }, {id: 1, age: 21 }, ], 'id'); // 正常运行 groupBy([ {id: 1, name: 'Marc', isAlive: false }, {id: 1, age: 21 }, ], 'name'); // 正常运行 groupBy([ {id: 1, name: 'Marc', isAlive: false }, {id: 1, age: 21 }, ], 'age'); // 正常运行
正确实现方案
可以通过自定义条件类型筛选出符合要求的键,同时确保TypeScript能正确推断Item[Key]的类型:
方案1:直接在参数上添加条件约束
function groupBy<Item, Key extends keyof Item>( items: Item[], key: Item[Key] extends PropertyKey ? Key : never ): Record<Extract<Item[Key], PropertyKey>, Item> { const groups = {} as Record<Extract<Item[Key], PropertyKey>, Item>; for (const item of items) { const groupKey = item[key] as Extract<Item[Key], PropertyKey>; groups[groupKey] = { ...groups[groupKey], ...item }; } return groups; }
方案2:提前定义筛选键的类型
// 定义类型:筛选出对象中值为PropertyKey的键 type PropertyKeyKeys<T> = { [K in keyof T]: T[K] extends PropertyKey ? K : never }[keyof T]; function groupBy<Item, Key extends PropertyKeyKeys<Item>>( items: Item[], key: Key ): Record<Item[Key], Item> { const groups = {} as Record<Item[Key], Item>; for (const item of items) { groups[item[key]] = { ...groups[item[key]], ...item }; } return groups; }
错误原因分析
- 第一种实现:虽然用映射类型筛选了键,但TypeScript无法自动关联
Key和Item[Key]必须是PropertyKey的约束,导致Record的键类型不满足要求。 - 第二种实现:泛型顺序错误,
Item extends Record<Key, PropertyKey>会先推断Key,再强制Item符合该约束,但传入的数组对象包含额外属性,超出了Record<Key, PropertyKey>的范围,因此报错。
内容的提问来源于stack exchange,提问作者mgm793
相关产品推荐
相关产品推荐

