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
相关产品推荐
相关产品推荐

