我正在编写一个简单的 Axios 包装函数,它结合了 zod-parsing 并返回一个可区分的联合,以实现更愉快的错误处理。在某些情况下,Axios 的默认行为应该在它应该抛出任何错误的地方保留,我已经通过将
{ throw: true }
传递给 config
参数的重载成功地实现了这一点。我不喜欢的是实施的冗长。有没有办法让下面的重载更干?理想情况下声明泛型 Schema
和 Parsed
一次而不是重复它们三次?
import type { AxiosRequestConfig } from 'axios';
import * as z from 'zod';
import * as URLS from '~/constants/api-url-constants';
import { coerceError } from '~/utils';
type Result<T> = { ok: true; response: T } | { ok: false; error: Error };
type Url = string | ((urls: typeof URLS) => string);
interface Config<Schema extends z.Schema, Throw extends boolean>
extends AxiosRequestConfig {
schema?: Schema | ((zod: typeof z) => Schema);
throw?: Catch;
}
/**
* Axios wrapper which accepts Zod-schema to parse
* response data and return typed response inferred from schema's output type.
* Schema validations happens in `config.transformResponse`,
* meaning that failed Zod parsing is able to be intercepted.
*
* Returns discriminated union by default for error handling.
* Use overload `config.catch` to throw unsuccessful api-call like native axios.
*/
async function axiosWrapper<
Schema extends z.Schema = z.ZodUnknown,
Parsed = z.output<Schema>,
>(url: Url, config: Config<Schema, false>): Promise<Result<Parsed>>;
async function axiosWrapper<
Schema extends z.Schema = z.ZodUnknown,
Parsed = z.output<Schema>,
>(url: Url, config: Config<Schema, true>): Promise<Parsed>;
async function axiosWrapper<
Schema extends z.Schema = z.ZodUnknown,
Parsed = z.output<Schema>,
>(url: Url, config: Config<Schema, boolean>): Promise<Result<Parsed> | Parsed> {
// …implmentation
}
可以减少您必须编写的泛型数量,并且通常可以在此处删除重复代码。但是,它也会改变您使用该功能的方式。
例如,以下将起作用:
type AxiosWrapperFunction<
Schema extends z.Schema = z.ZodUnknown,
Parsed = z.output<Schema>
> = {
(url: Url, config: Config<Schema, false>): Promise<Result<Parsed>>;
(url: Url, config: Config<Schema, true>): Promise<Parsed>;
};
export const createAxiosWrapper = <
Schema extends z.Schema = z.ZodUnknown,
Parsed = z.output<Schema>
>(): AxiosWrapperFunction<Schema, Parsed> => {
return (url: Url, config: Config<Schema, boolean>) => {
/** implementation */
};
}
在这种情况下,我们必须创建一个包装函数,因为它也必须能够将泛型传递给实现函数。
缺点是,我们现在必须按如下方式使用函数:
const axiosWrapper = createAxiosWrapper</** your generics */>();
axiosWrapper(/** your arguments */)
而不是能够做到:
axiosWrapper</** your generics */>(/** your arguments */)
我的建议是继续重写泛型并坚持实现函数重载