通用方法的Typescript接口

时间:2019-07-19 15:05:03

标签: typescript interface

我最近开始使用TypeScript,因此是一名初学者:D。

对于应用程序,我使用以下通用方法从不同的端点获取JSON对象:

interface UnknownObject {
  [index: string]: UnknownObject;
}

const fetchJSON = async (
  url: string,
  body?: object
): Promise<UnknownObject> => {
  try {
    let response;
    if (!body) {
      response = await fetch(url);
    } else {
      response = await fetch(url, {
        method: "POST",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json"
        },
        body: JSON.stringify(body)
      });
    }
    const data = await response.json();
    if (response.status === 404) data.error = 404;
    console.log(data);
    return data;
  } catch (err) {
    throw err;
  }
};

export default fetchJSON;

我不太了解如何为该方法定义接口。

当前(请参阅代码)我基本上通过带有递归接口的Typescript禁用了Typescript,该递归接口告诉“这是一个可能带有子对象的对象”。 (由于不推荐,因此我已禁用该类型。)

一旦我在代码中使用该方法,便可以定义该接口。

例如,调用获取页面对象的方法可能像这样:fetchJSON("pageurl"): PageProps。定义函数本身时无需声明接口。我没有找到任何办法。

我的另一个想法是根据所使用的端点创建不同的获取方法。例如,fetchPageJSON,fetchUserJSON等。这样,我将始终知道应该返回哪个对象,但是我必须多次编写相同的方法。

执行此操作的正确方法是什么?

1 个答案:

答案 0 :(得分:3)

您的UnknownObject不仅比unknown甚至any更具表现力,而且自JSON.parse() in the standard library returns any起,我想您的fetchJSON应该只需返回Promise<any>

declare function fetchJSON(url: string, body?: object): Promise<any>;

我将假定您将信任端点以返回符合期望类型的类型的对象,并且不对端点结果进行任何运行时验证。由于编译器将无法验证从JSON反序列化的响应的形状是否正确,因此您需要使用type assertions来告诉编译器保证您的责任是 方式。当然,如果端点返回了意外的东西,那么您将对编译器撒谎,编译器将对库的用户说谎,而这些用户在运行时代码爆炸时将不满意。

以这些接口为例,让我们看看如何做到这一点:

interface User {
  name: string;
  age: number;
}

interface Page {
  url: string;
  body: string;
}

好吧,正如您所说,可以为每种类型使用不同的fetch函数,假设每种类型的URL都是不同的:

const fetchUser = async (body?: object): Promise<User> =>
  (await fetchJSON("/theUserURL", body)) as User;

const fetchPage = async (body?: object): Promise<Page> =>
  (await fetchJSON("/thePageURL", body)) as Page;

请注意,您不必多次实施fetchJSON,而只需调用它即可。您会看到as Useras Page类型的断言。


现在,如果url的可能值是常量string literals,而不是需要计算或解析的值,那么您可以将url-类型映射表示为它自己的接口:< / p>

interface FetchMap {
  "/theUserURL": User;
  "/thePageURL": Page;
  // ... etc
}

您从不使用该类型的实际值,但可以使用它来定义fetchJSON的版本,该版本仅允许您传递那些已知的URL:

const fetchObject: <U extends keyof FetchMap>(
  url: U,
  body?: object
) => Promise<FetchMap[U]> = fetchJSON;

const pagePromise = fetchObject("/thePageURL"); // Promise<Page>
const userPromise = fetchObject("/theUserURL"); // Promise<User>
const notAllowed = fetchObject("/aDifferentURL"); // error!

或者,如果您仍然希望允许传入未知网址,则可以使用条件类型为

的签名
const fetchSomething: <U extends string>(
  url: U,
  body?: object
) => Promise<U extends keyof FetchMap ? FetchMap[U] : any> = fetchJSON;

const alsoPagePromise = fetchSomething("/thePageURL"); // Promise<Page>
const alsoUserPromise = fetchSomething("/theUserURL"); // Promise<User>
const whoKnowsPromise = fetchSomething("/aDifferentURL"); // Promise<any>

fetchObjectfetchSomething函数仍在使用类型断言的等效项(因为any可以静默转换为nay类型),但是它们只允许您使用一个函数多个。


无论如何,希望能给您一些想法;祝你好运!

Link to code