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

TypeScript:如何实现基于字符串属性名与泛型的实体查询函数并保留类型推断?

嘿,我懂你现在在F#和TypeScript的类型约束这块有点晕,尤其是想搞一个能精准从状态里捞对应实体的泛型函数对吧?咱们一步步把这个问题捋清楚,既能保留类型推断,还能让类型定义更灵活~

首先先把基础类型补全(方便后续代码能跑通),假设你的实体类型都带id属性:

// 客户实体类型
interface Client {
  id: number;
  name: string; // 举个例子,你可以换成自己的属性
}

// 预约实体类型
interface Appointment {
  id: number;
  date: Date; // 同样是示例属性
}

// 应用状态接口(注意首字母大写,TypeScript类型规范)
interface State {
  clients: Client[];
  appointments: Appointment[];
  // 其他非实体列表属性可以放在这,比如 userSettings: UserSettings 之类的
}

// 初始化状态
const ApplicationState: State = {
  clients: [],
  appointments: [],
  // 其他属性初始化
};

第一个问题:实现带类型推断的泛型获取函数

你想要的getData函数,核心是让TypeScript知道:你传入的属性名对应哪种实体类型,返回值就应该是该实体(或undefined,因为可能找不到)。这里用泛型就能完美解决:

// 先定义允许的实体键类型——后面会优化这个,先看函数怎么写
type EntityKey = "clients" | "appointments";

const getData = <K extends EntityKey>(key: K, id: number): State[K][number] | undefined => {
  // 用find而不是filter,因为你要的是单个实体,不是数组
  return ApplicationState[key].find(entity => entity.id === id);
};

现在调用这个函数时,TypeScript会自动推断返回类型:

const myClient = getData('clients', 2);
// myClient的类型是 Client | undefined,能自动提示Client的属性
if (myClient) {
  console.log(myClient.name); // 完全有类型提示,不会报错
}

const myAppointment = getData('appointments', 5);
// myAppointment的类型是 Appointment | undefined

第二个问题:更优的属性类型定义

你手动写"clients" | "appointments"虽然能用,但以后如果在State里加了新的实体列表(比如invoices: Invoice[]),就得手动更新这个类型,很麻烦。咱们可以用映射类型让TypeScript自动推导符合条件的键名:

// 自动提取State中「值是带id属性的实体数组」的键名
type EntityKey = {
  [K in keyof State]: State[K] extends { id: number }[] ? K : never;
}[keyof State];

这个逻辑是:遍历State的所有键,判断对应的值是不是「包含id属性的对象数组」,如果是就保留这个键名,否则排除。这样以后你给State加新的实体列表属性,EntityKey会自动更新,完全不用手动维护!

比如你加个invoices: Invoice[](Invoice带id),EntityKey会自动变成"clients" | "appointments" | "invoices",省心得很。

最后再整合一下完整代码

interface Client {
  id: number;
  name: string;
}

interface Appointment {
  id: number;
  date: Date;
}

interface State {
  clients: Client[];
  appointments: Appointment[];
  // 示例:加个新的实体列表,EntityKey会自动包含它
  // invoices: Invoice[];
}

const ApplicationState: State = {
  clients: [],
  appointments: [],
};

// 自动推导的实体键类型
type EntityKey = {
  [K in keyof State]: State[K] extends { id: number }[] ? K : never;
}[keyof State];

// 带类型推断的泛型获取函数
const getData = <K extends EntityKey>(key: K, id: number): State[K][number] | undefined => {
  return ApplicationState[key].find(entity => entity.id === id);
};

这样既满足了你的需求,还兼顾了类型安全和扩展性,完全保留了TypeScript的类型推断能力~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.01 00:47:35