如何优化TypeScript泛型函数中Zod解析JSON响应的实现?
问题与解决方案
问题描述
我有一个用于请求不同URL并获取JSON响应的函数,希望使用Zod对响应进行验证。请问是否存在比示例更优的实现方式,无需在每次调用函数时传入Zod Schema?同时能否避免使用as T类型断言?
原示例代码:
import * as z from "zod"; // Zod 校验 Schema const MyResponse = z.strictObject({ key: z.string() }); // TypeScript 类型 type MyResponse = z.infer<typeof MyResponse>; // 带泛型的请求函数 async function request<T>(schema: z.ZodObject, url: string): Promise<T> { const response = await fetch(url); return schema.parse(await response.json()) /*as T*/; } // 调用函数 await request<MyResponse>(MyResponse, '/');
优化方案
方案1:自动从Schema推断类型,消除手动泛型与类型断言
调整泛型参数,让它直接关联Zod Schema的类型,TypeScript会自动推导返回值类型,无需手动指定泛型或使用as T:
import * as z from "zod"; const MyResponse = z.strictObject({ key: z.string() }); // 泛型绑定到Zod Schema,自动推断返回类型 async function request<Schema extends z.ZodTypeAny>( schema: Schema, url: string ): Promise<z.infer<Schema>> { const response = await fetch(url); const data = await response.json(); return schema.parse(data); // 无需断言,类型自动匹配 } // 调用时仅需传入Schema和URL,类型自动推导 const result = await request(MyResponse, '/'); // result 类型自动为 { key: string }
方案2:预定义绑定Schema的专用请求函数
如果某些接口的Schema固定,可以提前创建绑定好Schema的请求函数,后续调用仅需传入URL:
import * as z from "zod"; const MyResponse = z.strictObject({ key: z.string() }); // 通用基础请求函数 async function requestWithSchema<Schema extends z.ZodTypeAny>( schema: Schema, url: string ): Promise<z.infer<Schema>> { const response = await fetch(url); const data = await response.json(); return schema.parse(data); } // 预定义绑定MyResponse的请求函数 const requestMyResponse = (url: string) => requestWithSchema(MyResponse, url); // 调用时只需传URL const result = await requestMyResponse('/');
方案3:工厂函数批量生成专用请求器
如果有多个固定Schema的接口,用工厂函数批量生成绑定对应Schema的请求函数,彻底避免重复传Schema:
import * as z from "zod"; // 请求函数工厂:传入Schema,返回绑定该Schema的请求函数 function createRequester<Schema extends z.ZodTypeAny>(schema: Schema) { return async (url: string): Promise<z.infer<Schema>> => { const response = await fetch(url); const data = await response.json(); return schema.parse(data); }; } // 生成不同Schema的请求函数 const MyResponse = z.strictObject({ key: z.string() }); const requestMyResponse = createRequester(MyResponse); const AnotherResponse = z.strictObject({ id: z.number() }); const requestAnotherResponse = createRequester(AnotherResponse); // 调用示例 const myResult = await requestMyResponse('/'); const anotherResult = await requestAnotherResponse('/another');
以上方案均无需每次调用传Schema,且完全避免了as T类型断言,同时保留了完整的类型安全校验。
内容的提问来源于stack exchange,提问作者dennis
相关产品推荐
相关产品推荐

