TypeScript通用函数返回类型问题

时间:2019-09-04 14:24:21

标签: typescript

我想通过TypeScript泛型定义函数返回类型。因此R可以是我要定义的任何内容。

... Promise<R | string>对我来说不是解决方法

错误

错误:(29,9)TS2322:类型'string'无法分配给类型'R'。   'string'可分配给'R'类型的约束,但是'R'可以使用约束'{}'的其他子类型实例化。

import { isString, } from '@redred/helpers';

interface P {
  as?: 'json' | 'text';
  body?: FormData | URLSearchParams | null | string;
  headers?: Array<Array<string>> | Headers | { [name: string]: string };
  method?: string;
  queries?: { [name: string]: string };
}

async function createRequest<R> (url: URL | string, { as, queries, ...parameters }: P): Promise<R> {
  if (isString(url)) {
    url = new URL(url);
  }

  if (queries) {
    for (const name in queries) {
      url.searchParams.set(name, queries[name]);
    }
  }

  const response = await fetch(url.toString(), parameters);

  if (response.ok) {
    switch (as) {
      case 'json':
        return response.json();
      case 'text':
        return response.text(); // <- Error
      default:
        return response.json();
    }
  }

  throw new Error('!');
}

export default createRequest;

1 个答案:

答案 0 :(得分:2)

我可能会用overloads来表示与调用方的区别……如果调用方指定了"text",则返回类型肯定是Promise<string>,并且该函数不是通用的在R中。

此外:TypeScript命名约定通常为通用类型参数(尤其是TUKP保留单字符大写名称,因此我将将您的P扩展到Params。同样,标识符as也是有问题的,因为它是TypeScript中的保留字,可能会使IDE或编译器感到困惑。接下来,我将as替换为az。好的,您的界面是

interface Params {
  az?: "json" | "text";
  body?: FormData | URLSearchParams | null | string;
  headers?: Array<Array<string>> | Headers | { [name: string]: string };
  method?: string;
  queries?: { [name: string]: string };
}

这是我要使用的重载。一个非通用的呼叫签名仅接受az的{​​{1}},另一个在"text"中通用,并且仅接受R或{ {1}} /丢失。 实现签名可以涉及az"json"或任何您想要的东西,因为它在调用方的侧面是不可见的。

undefined

这是我们使用它获取文本的方式:

R | string

这是我们使用它来获取其他类型的方法(这要求调用者指定any,因为无法推断出它):

async function createRequest(
  url: URL | string,
  { az, queries, ...parameters }: Params & { az: "text" }
): Promise<string>;
async function createRequest<R>(
  url: URL | string,
  { az, queries, ...parameters }: Params & { az?: "json" }
): Promise<R>;
async function createRequest<R>(
  url: URL | string,
  { az, queries, ...parameters }: Params
): Promise<R | string> {
  if (isString(url)) {
    url = new URL(url);
  }

  if (queries) {
    for (const name in queries) {
      url.searchParams.set(name, queries[name]);
    }
  }

  const response = await fetch(url.toString(), parameters);

  if (response.ok) {
    switch (az) {
      case "json":
        return response.json();
      case "text":
        return response.text(); // <- okay now
      default:
        return response.json();
    }
  }

  throw new Error("!");
}

请注意,如果您指定了const promiseString = createRequest("str", { az: "text" }); // Promise<string> ,则不能要求R

interface Dog {
  name: string;
  age: number;
  breed: string;
  fleas: boolean;
}

const promiseDog = createRequest<Dog>("dog", {}); // Promise<Dog>

好的,希望对您有所帮助。祝你好运!

Link to code