如何实现通用TypeScript排序函数:筛选指定类型属性并规避类型错误
通用字符串属性排序函数的类型问题解决
我需要实现一个通用排序函数,核心需求如下:
- 接收元素类型为
T的数组,T至少包含一个字符串类型(含可选字符串)的属性 - 允许传入
T中符合字符串类型的任意属性作为排序键,编辑器智能提示仅显示符合条件的属性,函数体内无需硬编码具体键名以保证复用性
但实现时遇到诸多问题:调用端出现类型不匹配错误(如传入lastName时报错)、函数体出现any类型、或被迫指定具体键名降低复用性。以下是最接近可行的代码:
type User = { name: string; lastName?: string age: number } const myUsers: User[] = [{age: 32, name: "Foo"}] type FilterBy<T, U> = { [Key in keyof T as T[Key] extends U ? Key : never]: T[Key]; }; type InferValueFromArray<T extends any[]> = T extends Array<infer R> ? R : never; const sortingBy = <T extends { [name: string]: string | undefined }[], K extends keyof FilterBy<InferValueFromArray<T>, string|undefined>>(data: T, key: K) => { return data.sort((a, b) => (a[key] || '').localeCompare(b[key] || '')); }; // 报错:Type 'number' is not assignable to type 'string' const mySortedUsers = sortingBy(myUsers, 'lastName');
问题分析
原代码中泛型T的约束T extends { [name: string]: string | undefined }[]强制数组元素的所有属性必须是string | undefined,但User类型包含age: number属性,导致myUsers不符合该约束,进而引发类型推断混乱,出现错误。
修正方案
调整泛型设计,优化类型过滤逻辑,代码如下:
type User = { name: string; lastName?: string; age: number; }; const myUsers: User[] = [{ age: 32, name: "Foo" }]; // 过滤出T中值为string或string|undefined类型的键 type StringKeys<T> = { [K in keyof T]: T[K] extends string | undefined ? K : never; }[keyof T]; const sortingBy = <T>(data: T[], key: StringKeys<T>) => { // 创建数组副本避免修改原数组,可选但更安全 return [...data].sort((a, b) => { // 使用??处理undefined,比||更严谨(不会误判空字符串) const valA = a[key] ?? ''; const valB = b[key] ?? ''; return valA.localeCompare(valB); }); }; // 正常工作,智能提示仅显示name、lastName const mySortedUsers = sortingBy(myUsers, 'lastName');
关键优化点
StringKeys<T>类型:遍历T的所有键,仅保留值类型为string或string | undefined的键,确保智能提示只展示符合条件的属性- 泛型约束简化:将泛型改为
<T>,直接接收T[]数组,避免原约束对元素类型的过度限制 - 避免原数组修改:使用
[...data]创建副本后再排序,符合函数式编程的无副作用原则 - 更严谨的空值处理:用
??替代||处理undefined,不会把空字符串当成假值替换
内容的提问来源于stack exchange,提问作者Martin Hansson
相关产品推荐
相关产品推荐

