TypeScript中readQuery函数根据fields参数动态返回类型的实现
实现方案:泛型+条件类型+字面量推断
直接用泛型结合条件类型就能精准约束返回类型,比重载更简洁且类型推断更准确。核心是让fields参数的类型被推断为具体的键字面量数组,再通过条件类型动态生成返回类型。
步骤1:定义基础类型(示例)
先给出ERPTypes的示例结构,方便后续演示:
// 示例ERP实体类型 type ERPTypes = { User: { id: number; name: string; email: string }; Order: { orderId: string; amount: number; status: string }; };
步骤2:定义函数的泛型参数与条件类型
函数需要两个泛型:
T:指定操作的ERP实体类型(比如"User"或"Order")K:可选,指定fields的具体键字面量,默认是keyof ERPTypes[T]
通过条件类型判断是否传入了fields,动态决定返回类型:
type ReadQueryReturn<T extends keyof ERPTypes, K extends keyof ERPTypes[T]> = K extends never ? Promise<ERPTypes[T][]> : Promise<Pick<ERPTypes[T], K>[]>;
步骤3:实现函数并约束参数
函数参数要让TypeScript能推断出K的具体字面量类型,所以fields需要定义为readonly K[](用readonly确保字面量类型不被拓宽):
async function readQuery< T extends keyof ERPTypes, K extends keyof ERPTypes[T] = keyof ERPTypes[T] >(payload: { entity: T; fields?: readonly K[]; // 其他业务参数可在此添加 }): ReadQueryReturn<T, K> { // 实际业务逻辑示例:调用接口获取数据 const rawData = await fetch(`/api/${payload.entity}`).then(res => res.json()); if (payload.fields) { return rawData.map(item => { const pickedItem: Partial<Pick<ERPTypes[T], K>> = {}; payload.fields.forEach(key => { pickedItem[key] = item[key]; }); return pickedItem as Pick<ERPTypes[T], K>; }) as ReadQueryReturn<T, K>; } return rawData as ReadQueryReturn<T, K>; }
测试验证
调用时TypeScript会自动根据是否传入fields推断返回类型:
// 未传fields,返回Promise<User[]> const allUsers = await readQuery({ entity: "User" }); allUsers[0].id; // ✅ 正常访问所有属性 // 传fields,返回Promise<Pick<User, "id" | "name">[]> const partialUsers = await readQuery({ entity: "User", fields: ["id", "name"] }); partialUsers[0].id; // ✅ 可访问id、name partialUsers[0].email; // ❌ 类型报错,email不在返回类型中
为什么比重载更优?
重载需要手动编写多个函数签名,且无法自动捕获fields的具体键字面量(除非每个重载都手动指定键,扩展性极差)。而泛型+条件类型的方案能自动推断fields的具体键,同时动态生成返回类型,代码更简洁,类型约束更精准。
内容的提问来源于stack exchange,提问作者Ilijanovic
相关产品推荐
相关产品推荐

