首页 > 解决方案 > 使用 Typescript 的 SWR Vercel 全局配置

问题描述

我想用 SWR Configs 创建一个对象来将它们设置为全局。我正在使用来自 swr 的 ConfigInterface 类型。

import { ConfigInterface } from 'swr';

import fetcher from 'api/axiosFetcher';

const swrConfig: ConfigInterface = {
  fetcher,
};

export default swrConfig;

问题是当我尝试在 SWRConfig 中使用这个对象时

<SWRConfig value={swrConfig}>

显示下一个错误

Type 'ConfigInterface<any, any, fetcherFn<any>>' is not assignable to type 'ConfigInterface<any, any, (...args: any) => any>'.
  Types of property 'fetcher' are incompatible.
    Type '((...args: any) => any) | null | undefined' is not assignable to type '((...args: any) => any) | undefined'.
      Type 'null' is not assignable to type '((...args: any) => any) | undefined'.ts(2322)

我的提取器功能

const fetcher = <T>(url: string): Promise<T> =>
  axiosInstance
    .get(url)
    .then((response) => {
      if (response.statusText === 'OK') {
        return response.data;
      }
      throw new Error(response.statusText);
    })
    .catch(({ response }) => {
      throw new Error(response.statusText);
    });

标签: reactjstypescriptswr

解决方案


为您的问题编写了一些自定义 TypeDef。

  • 首先,将您的函数编写为 TypeScript Class,它接受泛型参数 (T)。这样,您可以在应用了 Mixin 的类上应用约束 (T)
type FetcherConstructor<T = {}> = new (...args: any[]) => T;

  • 我们的类只有在 url 参数传递一个字符串值时才有效——所需的 url 参数实现应用约束的示例(如上所述)
type FetcherAbstracted = FetcherConstructor<{
    fetcher: (url: string, init?: RequestInit) => Promise<Response>;
}>;

如您所见,有条件的 RequestInit 接口相当广泛

interface RequestInit {
    /**
     * A BodyInit object or null to set request's body.
     */
    body?: BodyInit | null;
    /**
     * A string indicating how the request will interact with the browser's cache to set request's cache.
     */
    cache?: RequestCache;
    /**
     * A string indicating whether credentials will be sent with the request always, never, or only when sent to a same-origin URL. Sets request's credentials.
     */
    credentials?: RequestCredentials;
    /**
     * A Headers object, an object literal, or an array of two-item arrays to set request's headers.
     */
    headers?: HeadersInit;
    /**
     * A cryptographic hash of the resource to be fetched by request. Sets request's integrity.
     */
    integrity?: string;
    /**
     * A boolean to set request's keepalive.
     */
    keepalive?: boolean;
    /**
     * A string to set request's method.
     */
    method?: string;
    /**
     * A string to indicate whether the request will use CORS, or will be restricted to same-origin URLs. Sets request's mode.
     */
    mode?: RequestMode;
    /**
     * A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect.
     */
    redirect?: RequestRedirect;
    /**
     * A string whose value is a same-origin URL, "about:client", or the empty string, to set request's referrer.
     */
    referrer?: string;
    /**
     * A referrer policy to set request's referrerPolicy.
     */
    referrerPolicy?: ReferrerPolicy;
    /**
     * An AbortSignal to set request's signal.
     */
    signal?: AbortSignal | null;
    /**
     * Can only be null. Used to disassociate request from any Window.
     */
    window?: any;
}
  • 其中bodyInit类型定义如下
type BodyInit = Blob | BufferSource | FormData | URLSearchParams | ReadableStream<Uint8Array> | string;
  • 并且本示例中的Response接口typeof fetcher具有以下响应接口作为其返回的有效负载
/** This Fetch API interface represents the response to a request. */
interface Response extends Body {
    readonly headers: Headers;
    readonly ok: boolean;
    readonly redirected: boolean;
    readonly status: number;
    readonly statusText: string;
    readonly trailer: Promise<Headers>;
    readonly type: ResponseType;
    readonly url: string;
    clone(): Response;
}

在这里查看mixins以获得更多说明。

  • 一个简单的实际实现
export default async function FetcherExample(
    fetch: FetcherAbstracted
) {
    const exactPath = 'docs/handbook/mixins.html';

    const data: Response = await new fetch().fetcher(
        `https://typescriptlang.org${exactPath}`,
        {
            body: exactPath,
            method: 'GET',
            headers: {
                'Content-Type': 'application/json'
                // ...
            },
            credentials: "include"
        }
    );

    return await data.json();
}

为清楚起见,现在将所有内容放在一个代码片段中

export type FetcherConstructor<T = {}> = new (...args: any[]) => T;

export type FetcherAbstracted = FetcherConstructor<{
    fetcher: (url: string, init?: RequestInit) => Promise<Response>;
}>;

export default async function FetcherExample(
    fetch: FetcherAbstracted
) {
    const exactPath = 'docs/handbook/mixins.html';

    const data: Response = await new fetch().fetcher(
        `https://typescriptlang.org${exactPath}`,
        {
            body: exactPath,
            method: 'GET',
            headers: {
                'Content-Type': 'application/json'
                // ...
            },
            credentials: "include"
        }
    );

    return await data.json();
}

更新

为了简明扼要地回答你的问题,这个钩子应该与 axios+SWR 一起使用,这类似于我上面写的参数约束泛型

type FetcherAbstracted = FetcherConstructor<{
    fetcher: (url: string, init?: RequestInit) => Promise<Response>;
}>;

如此之多,以至于您可以将其用于您的 Axios fetcher 实现。

const fetcher: FetcherAbstracted = async (url: string) => {
  try {
    const res = await Axios.get(url);
    return res.data;
  } catch (error) {
    throw error.response.data;
  }
};

推荐阅读