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

如何通过TypeScript类型签名约束排序函数的对象属性类型?

问题描述

我正在实现一个类Lodash sortBy 的排序比较函数,要求:

  • 参与比较的两个对象必须是同一类型
  • 指定用于排序的属性类型只能是 string 或 number
  • 两个对象的该属性类型必须完全一致(要么都是string,要么都是number)

我希望通过TypeScript的类型签名来实现这些约束,替代当前代码里的运行时检查,但不知道具体怎么写泛型和类型约束。当前的实现代码如下:

compareObjectsForSorting(a: object, b: object, propertyName: string, sortDescending = false): number {
  /* First, some safety checks to make sure the arguments match what's intended.
  Check that the desired property is even present on both objects, and throw an error if not */
  [a, b].forEach((obj) => {
    if (!Object.keys(obj).includes(propertyName)) {
      throw new Error(propertyName + 'not found in object' + obj);
    }
  });
  /* Let's just stick to comparing numbers and strings */
  const typeOfPropA = typeof a[propertyName];
  const typeOfPropB = typeof b[propertyName];
  [typeOfPropA, typeOfPropB].forEach((t) => {
    if (t !== 'string' && t !== 'number') {
      throw new Error(`${propertyName} is of type ${t}. Please compare only strings or numbers.`);
    }
  });
  /* And finally, let's make sure the same property is of the same type in both objects */
  if (typeOfPropA !== typeOfPropB) {
    throw new Error(`Unable to compare. ${propertyName} is a ${typeof a[propertyName]} 
    in one object, and a ${typeof b[propertyName]} in another.`);
  }
  // ... and then comes the actual logic of comparing, not shown here.
}
解决方案

可以通过泛型+索引类型约束实现这些要求,让TypeScript在编译阶段就完成类型检查,无需额外运行时判断:

function compareObjectsForSorting<T, K extends keyof T>(
  a: T,
  b: T,
  propertyName: K,
  sortDescending = false
): number {
  const valA = a[propertyName];
  const valB = b[propertyName];

  // 仅当T为any类型时需要兜底检查,明确类型可省略
  if (typeof valA !== 'string' && typeof valA !== 'number') {
    throw new Error(`${String(propertyName)} must be string or number`);
  }

  // 实际比较逻辑
  let result: number;
  if (typeof valA === 'string') {
    result = valA.localeCompare(valB as string);
  } else {
    result = valA - valB;
  }

  return sortDescending ? -result : result;
}

关键类型说明

  • 泛型T:强制a和b为同一类型,确保两者属性结构完全一致。
  • K extends keyof T:约束propertyName必须是T的有效属性名,避免传入不存在的属性。
  • 自动类型推导:传入具体对象和属性名时,TS会自动推导T和K的类型,同时校验T[K]是否为string/number——不符合要求的话编译阶段直接报错。

使用示例

interface User {
  name: string;
  age: number;
}

const user1: User = { name: "Alice", age: 30 };
const user2: User = { name: "Bob", age: 25 };

// 合法调用:属性存在且类型符合要求
compareObjectsForSorting(user1, user2, "name");
compareObjectsForSorting(user1, user2, "age", true);

// 编译报错:属性不存在
compareObjectsForSorting(user1, user2, "invalidProp");

// 编译报错:属性类型不是string/number
interface BadUser {
  active: boolean;
}
const u1: BadUser = { active: true };
const u2: BadUser = { active: false };
compareObjectsForSorting(u1, u2, "active");

补充说明

如果你的T都是明确的接口类型,编译阶段的检查已经足够,可以完全移除运行时的类型判断;只有当T可能为any时,才需要保留兜底检查。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 12:50:09