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

如何让SortItemConfig的compareFn参数类型与field返回类型自动匹配?

解决TypeScript排序配置中field与compareFn的类型一致性问题

现有类型定义

当前用于排序配置的TypeScript类型如下:

export type SortConfig<T> = ReadonlyArray<SortItemConfig<T>>;
export interface SortItemConfig<T> {
  field: (obj: T) => any;
  direction: SortDirection;
  compareFn?: SortFn<any>;
}

export const SORT_DIRECTIONS = {
  ASC: 'asc',
  DESC: 'desc',
} as const;

export type ValueOf<T> = T[keyof T];
export type SortDirection = ValueOf<typeof SORT_DIRECTIONS>;
export type SortFn<T> = (a: T, b: T) => number;

需求目标

希望SortItemConfig中field的返回类型与compareFn的输入类型自动保持一致。例如在以下示例中:

interface Player {
  name: string;
  character: 'human' | 'alien';
  rank: number;
  stats: PlayerStats;
}

interface PlayerStats {
  wins: number;
  loses: number;
  totalMatches: number;
  notes?: string;
}

const sortConfig: SortConfig<Player> = [
  {
    field: (player) => player.rank,
    direction: SORT_DIRECTIONS.ASC,
    // 期望rank1和rank2自动推断为number类型
    compareFn: (rank1, rank2) => {
      return rank1 - rank2;
    }
  },
  {
    field: (player) => player.stats,
    direction: SORT_DIRECTIONS.ASC,
    // 期望stat1和stat2自动推断为PlayerStats类型
    compareFn: (stat1, stat2) => {
      return stat1.wins - stat2.wins;
    }
  }
];

尝试过的无效方案

曾尝试为SortItemConfig添加泛型参数R,但由于SortConfig直接指定R为any,导致类型推断失效:

export type SortConfig<T> = ReadonlyArray<SortItemConfig<T, any>>;
export interface SortItemConfig<T, R> {
  field: (obj: T) => R;
  direction: SortDirection;
  compareFn?: SortFn<R>;
}

实际使用场景(更新说明)

排序配置会被用于Sorting<T>类,生成针对T类型的排序函数:

export class Sorting<T> {
  private readonly sortConfig: SortConfig<T>;

  constructor(sortConfig: SortConfig<T>) {
    this.sortConfig = sortConfig;
  }

  public getSortFn() {
    return (objA: T, objB: T): number => {
      for (const sort of this.sortConfig) {
        const compare = this.getCompareFn(sort);

        let result = compare(sort.field(objA), sort.field(objB));
        result = isAscending(sort.direction) ? result : -result;

        if (result !== 0) {
          return result;
        }
      }

      return 0;
    };
  }

  protected getCompareFn(sort: SortItemConfig<T>): SortFn<any> {
    return sort.compareFn ?? compareValues;
  }
}

function isAscending(sortDirection: SortDirection): boolean {
  return sortDirection === SORT_DIRECTIONS.ASC;
}

function compareValues(a: unknown, b: unknown): number {
  // 占位实现
  return -1;
}

正确解决方案

1. 修改类型定义

保留SortItemConfig的泛型参数R,并将SortConfig定义为支持任意R的数组类型:

export type SortConfig<T> = ReadonlyArray<SortItemConfig<T, unknown>>;
export interface SortItemConfig<T, R> {
  field: (obj: T) => R;
  direction: SortDirection;
  compareFn?: SortFn<R>;
}

2. 添加辅助推断函数

直接声明SortConfig<T>变量会导致TypeScript无法推断每个元素的具体R类型,因此需要创建一个辅助函数来触发逐个元素的类型推断:

export function createSortConfig<T>(config: readonly SortItemConfig<T, any>[]): SortConfig<T> {
  return config;
}

3. 使用辅助函数创建配置

通过createSortConfig函数创建排序配置,TypeScript会自动为每个元素推断field返回类型与compareFn输入类型的一致性:

const sortConfig = createSortConfig<Player>([
  {
    field: (player) => player.rank,
    direction: SORT_DIRECTIONS.ASC,
    compareFn: (rank1, rank2) => rank1 - rank2 // rank1、rank2自动推断为number
  },
  {
    field: (player) => player.stats,
    direction: SORT_DIRECTIONS.ASC,
    compareFn: (stat1, stat2) => stat1.wins - stat2.wins // stat1、stat2自动推断为PlayerStats
  }
]);

4. 更新Sorting类类型(可选)

为了让Sorting类的类型更严谨,可以修改getCompareFn为泛型方法:

export class Sorting<T> {
  private readonly sortConfig: SortConfig<T>;

  constructor(sortConfig: SortConfig<T>) {
    this.sortConfig = sortConfig;
  }

  public getSortFn() {
    return (objA: T, objB: T): number => {
      for (const sort of this.sortConfig) {
        const compare = this.getCompareFn(sort);
        const valA = sort.field(objA);
        const valB = sort.field(objB);
        let result = compare(valA, valB);
        result = isAscending(sort.direction) ? result : -result;
        if (result !== 0) {
          return result;
        }
      }
      return 0;
    };
  }

  protected getCompareFn<R>(sort: SortItemConfig<T, R>): SortFn<R> {
    // 类型断言确保默认比较函数符合SortFn<R>类型
    return sort.compareFn ?? (compareValues as SortFn<R>);
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 18:44:51