You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript:如何优化基于字符串类型键的实体排序函数?

问题

我想实现一个能对任意类型实体列表排序的函数:第一个参数是任意类型的对象数组,第二个参数必须是实体类型中值为string的键(因为要用到localeCompare做比较)。目前的代码能正常运行,但必须给keyA和keyB添加as string类型断言,否则TypeScript会把它们推断为T[KeyWithStringValue<T>]而非string。按定义T[KeyWithStringValue<T>]应该等价于string,有没有更优的实现方式?

当前代码示例:

type PickByValue<T, ValueType> = { [Key in keyof T]: T[Key] extends ValueType ? Key : never }[keyof T];

type KeyWithStringValue<T> = PickByValue<T, string>;

export const sortEntityBy = <T>(entities: T[], key: KeyWithStringValue<T>): T[] => {
  if (!entities) return [];
  const sorted = [...entities];
  return sorted.sort((a, b) => {
    const keyA = a[key] as string;
    const keyB = b[key] as string;
    return keyA.localeCompare(keyB);
  });
};

示例验证:

type Country = {
  name: string;
  iso: string;
  id: number;
}

type CountryTypeString = KeyWithStringValue<Country>;

// CountryTypeString == "name" | "iso"
更优实现方式

问题根源是TypeScript对泛型的类型推断限制:虽然KeyWithStringValue<T>筛选出了值为string的键,但当T是泛型时,TypeScript无法直接确定T[KeyWithStringValue<T>]一定是string(比如存在联合类型的边界情况)。我们可以通过明确约束泛型的键值关系,让TypeScript直接推断出对应值的类型,从而去掉冗余的类型断言。

方案一:泛型参数直接约束

export const sortEntityBy = <T, Key extends keyof T>(
  entities: T[],
  key: Key extends keyof T ? (T[Key] extends string ? Key : never) : never
): T[] => {
  if (!entities) return [];
  const sorted = [...entities];
  return sorted.sort((a, b) => {
    // 此时TypeScript可直接推断a[key]、b[key]为string,无需断言
    return a[key].localeCompare(b[key]);
  });
};

方案二:优化类型定义

用Extract工具类型简化筛选逻辑,生成更精准的键类型:

// 筛选T中值为string的键
type StringKeyOf<T> = Extract<keyof T, { [K in keyof T]: T[K] extends string ? K : never }[keyof T]>;

export const sortEntityBy = <T>(entities: T[], key: StringKeyOf<T>): T[] => {
  if (!entities) return [];
  const sorted = [...entities];
  return sorted.sort((a, b) => a[key].localeCompare(b[key]));
};

效果验证

用Country类型测试:

const countries: Country[] = [
  { name: "Japan", iso: "JP", id: 3 },
  { name: "China", iso: "CN", id: 1 },
  { name: "USA", iso: "US", id: 2 }
];

// 正常运行,类型检查通过
const sortedByName = sortEntityBy(countries, "name");
const sortedByIso = sortEntityBy(countries, "iso");

// 传入非string值的键会触发类型报错,符合预期
// const sortedById = sortEntityBy(countries, "id"); // 类型错误

内容的提问来源于stack exchange,提问作者Bapt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 01:02:10