TypeScript使用泛型做索引时算术运算报错的原因是什么?
报错原因
TypeScript 对泛型函数的类型检查在函数定义阶段执行,不会结合后续的调用场景推导类型:
- 你定义的泛型签名为
function sortBy<K extends keyof T, T>(vals: T[], key: K): T[],其中T没有任何类型约束,可以是任意类型,对应的T[K]也可以是字符串、对象、函数等任意类型,而算术减法运算仅支持作用在number/bigint/枚举等类型上操作,因此在函数定义阶段就会抛出类型错误。 - 你编写的非泛型版本没有报错,是因为此时
T被固定为Point类型,key的取值范围是x | y,对应的属性值都是number类型,完全符合减法运算的类型要求。
你可能会疑惑调用时传入的是Point类型数组,为什么类型检查器不识别?因为泛型函数需要保证对所有符合签名的调用场景都合法,不会只针对某一次调用的传参类型放宽校验规则,在你没有明确约束T[K]为数字类型的前提下,类型检查器必须假设T[K]可能为任意不可进行算术操作的类型。
无需断言为
any的解决方案 方案1:约束泛型仅支持数字类型字段排序
直接给泛型增加类型约束,明确要求传入的T类型中,K对应的属性值必须是number类型即可:
interface Point { x: number y: number } // 调整泛型约束,要求T的K属性类型为number function sortBy<K extends PropertyKey, T extends Record<K, number>>(vals: T[], key: K): T[] { return vals.sort((a, b) => { return b[key] - a[key] }) } const pts: Point[] = [ { x: 1, y: 1 }, { x: 2, y: 0 }, ] console.log(sortBy(pts, "x"))
方案2:支持通用类型字段排序
如果需要兼容字符串、日期等更多类型的字段排序,可以扩展为支持传入自定义比较函数的版本:
function sortBy<K extends keyof T, T>( vals: T[], key: K, compare?: (a: T[K], b: T[K]) => number ): T[] { // 默认比较规则:对数字类型做减法比较 const defaultCompare = (a: T[K], b: T[K]) => (b as unknown as number) - (a as unknown as number) return vals.sort((a, b) => compare?.(a[key], b[key]) ?? defaultCompare(a[key], b[key]) ) } // 数字类型字段调用方式不变 console.log(sortBy(pts, "x")) // 字符串类型字段可以传入自定义比较函数 const users = [{name: '张三'}, {name: '李四'}] console.log(sortBy(users, 'name', (a, b) => a.localeCompare(b)))
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

