使用具有多种形状的函数的typescript类(重载)| 泛型

IT技术 reactjs typescript
2021-05-18 12:30:56

TYPESCRIPT 游乐场

存在诸如Overloadsfor 之类的东西class我创建了与 createRequest.ts 相关的先前问题,该函数应该没有错误。我想为函数中的class Data类似泛型定义相同的泛型createRequest

错误

错误:(31, 52) TS2345:“RequestParameters”类型的参数不可分配给“RequestParameters & { as?: "json" | 类型的参数 不明确的; }'。类型 'RequestParameters' 不能分配给类型 '{ as?: "json" | 不明确的; }'。'as' 的属性类型是不兼容的。输入'"json" | “文本” | undefined' 不能分配给类型 '"json" | 不明确的'。类型 '"text"' 不能分配给类型 '"json" | 不明确的'。

数据.tsx

import * as React from 'react';

import createRequest, { RequestParameters, } from '../../createRequest';

interface P<R> {
  children: (children: { createRequest: Data<R>['createRequest'] } & S<R>) => React.ReactNode;
  parameters: RequestParameters;
  url: Parameters<typeof createRequest>[0];
}

interface S<R> {
  error: Error | null;
  response: R | null;
}

class Data<R> extends React.Component<P<R>, S<R>> {
  async componentDidMount () {
    await this.createRequest();
  }

  state = { error: null, response: null, };

  createRequest = async (): Promise<void> => {
    this.setState(() => ({ error: null, response: null, }));

    try {
      const { parameters, url, } = this.props;

      const response = await createRequest<R>(url, parameters); // <-- Error

      this.onResponse(response);
    } catch (error) {
      this.onError(error);
    }
  };

  onError (error: Error): void {
    this.setState(() => ({ error, }));

    throw error;
  }

  onResponse (response: R): void {
    this.setState(() => ({ response, }));
  }

  render () {
    const { children, } = this.props;

    return children({ createRequest: this.createRequest, ...this.state, });
  }
}

export default Data;

创建请求文件

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

export interface RequestParameters {
  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 (url: URL | string, parameters: RequestParameters & { as: 'text' }): Promise<string>;
async function createRequest<R> (url: URL | string, parameters: RequestParameters & { as?: 'json' }): Promise<R>;
async function createRequest<R> (url: URL | string, parameters: RequestParameters): Promise<R | string> {
  if (isString(url)) {
    url = new URL(url);
  }

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

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

  if (response.ok) {
    switch (parameters.as) {
      case 'json':
        return response.json();
      case 'text':
        return response.text();
      default:
        return response.json();
    }
  }

  throw new Error('!');
}

export default createRequest;
1个回答

该函数createRequest()已重载,但您正试图对可能的两个重载使用一次调用,但这是行不通的。重载几乎就像来自调用方的不同函数......想象你有createRequestJson()createRequestText()考虑如何一次调用它们......它需要看起来像类型保护函数someTest(parameters) ? createRequestJson<R>(url, parameters) : createRequestText(url, parameters)在哪里someTest(parameters)其结果会缩小到合适的类型。parameters

解决这个问题的最简单的方法是通过断言parameters是泛型重载可接受的类型来推动编译器,例如通配符any类型:

const response = await createRequest<R>(url, parameters as any); // no error

任何其他修复都可能需要大量重构,因为您的Data<R>P<R>类型正在吞噬 json 请求和文本请求之间的区别。所以createRequest()关心这种区别(这就是你得到错误的原因),但调用代码中没有其他任何东西。 Data<R>非常高兴地表示R具有属性的非字符串,包括RequestParametersas属性为"text". 如果您对此感到满意,您不妨将断言推入 createRequest()以获取此非重载函数:

async function createRequest<R>(
  url: URL | string,
  parameters: RequestParameters
): Promise<R> {
  if (typeof url === "string") {
    url = new URL(url);
  }

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

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

  if (response.ok) {
    switch (parameters.as) {
      case "json":
        return response.json();
      case "text":
        return (response.text() as any) as R; // assertion
      default:
        return response.json();
    }
  }

  throw new Error("!");
}

并处理某人可以调用的事实createRequest<SomeRandomThing>(url, {as: "text"}),也许在文档中带有警告。坦率地说,我可能会走这条路,因为另一种选择是将所有类型和类拆分为 json 和文本风格。这并非不可能,如果您非常关心类型安全,您可以做到。但我怀疑您是否那么在意,因为createRequest<R>(url, {as: "json"})当调用者可以为R.

好的,希望有帮助;祝你好运!

代码链接