TypeScript约束泛型属性类型时出现索引报错的解决方案
TypeScript 限定泛型对象属性类型的实现方案
报错原因
你定义的KeysWithValsOfType工具类型筛选逻辑本身没有问题,但TypeScript泛型系统不会自动将K extends KeysWithValsOfType<T, string>的约束,反向推导为「K是T的合法索引键、且T[K]类型为string」的关联关系,因此在函数内部通过a[sortKey]访问属性时,编译器无法确认K可以合法索引T类型,才会抛出2536类型错误。
可直接运行的正确实现
两种写法都可以满足需求,核心是给泛型补全明确的索引关联约束:
写法1:简化泛型约束(推荐)
不需要额外定义工具类型,直接通过Record约束泛型关系,TS会自动推导合法的排序键范围:
interface Thing { id: string; price: number; test: number; other: { stuff: boolean }; } const list = [ { id: "1", price: 0, test: 2, other: { stuff: true }, }, { id: "12", price: 0, test: 2, other: { stuff: true }, }, ] as Array<Thing>; /** * 对对象数组按指定字符串属性排序 * 仅允许传入值为string类型的属性作为排序键 */ function doSomeSortingThingWithStringProperties<T extends Record<K, string>, K extends PropertyKey>( list: T[], sortKey: K ) { // 字符串比较推荐用localeCompare,比直接用<更严谨,支持本地化排序规则 list.sort((a, b) => a[sortKey].localeCompare(b[sortKey])); } doSomeSortingThingWithStringProperties(list, "id"); // 编译正常通过 doSomeSortingThingWithStringProperties(list, "other"); // 编译报错:类型"{ stuff: boolean; }"不满足约束"string" doSomeSortingThingWithStringProperties(list, "price"); // 编译报错:类型"number"不满足约束"string" console.log(list);
写法2:保留原有自定义工具类型
如果你需要复用KeysWithValsOfType的筛选逻辑,只需要在泛型约束中交叉keyof T,明确告知编译器K是T的合法键即可:
type KeysWithValsOfType<T, V> = keyof { [P in keyof T as T[P] extends V ? P : never]: P; }; function doSomeSortingThingWithStringProperties<T, K extends keyof T & KeysWithValsOfType<T, string>>( list: T[], sortKey: K ) { list.sort((a, b) => a[sortKey].localeCompare(b[sortKey])); }
校验效果
- 传入值为
string类型的属性(比如示例中的id)时,编译正常 - 传入值为非
string类型的属性(比如值为对象的other、值为数字的price/test)时,编译器会直接拦截报错,完全符合预期。
内容的提问来源于stack exchange,提问作者Stéphane Gerber
相关产品推荐
相关产品推荐

