繁体   English   中英

默认回调返回类型与应该被推断为该返回类型的泛型类型冲突

[英]Default callback return type conflicts with generic type which should be inferred as that return type

我正在研究一个 React 钩子,我遇到了一个问题,我想从传递给钩子的 function 的返回类型中推断出部分返回类型,但我并没有完全让它工作:

const useFetch = <ResponseType>(
  fetchInputFactory: () => RequestInfo | URL | [RequestInfo | URL, RequestInit],
  deps: DependencyList | undefined,
  transformResponse: (response: Response) => Promise<ResponseType> = async (response) => response
):
  | { loading: true }
  | { loading: false; success: true; response: ResponseType }
  | { loading: false; success: false; error: Error }

我得到的错误是:

(parameter) response: Response
Type 'Promise<Response>' is not assignable to type 'Promise<ResponseType>'.
  Type 'Response' is not assignable to type 'ResponseType'.
    'ResponseType' could be instantiated with an arbitrary type which could be unrelated to 'Response'.ts(2322)
index.ts(6, 22): The expected type comes from the return type of this signature.

不幸的是,泛型类型总是可以由消费者代码显式指定

因此,即使未提供第三个参数transformResponse ,它默认为异步标识 function,我们预计 TypeScript 会推断ResponseType只是Response ,此推断可能会被消费代码覆盖:

const f = useFetch<number>(someFetchInputFactoryFn, []);
//    ^? { loading: true } | { loading: false; success: true; response: number } | { loading: false; success: false; error: Error }
// TS expects `response` to be a number, but it should be a `Response`...

限制泛型类型是不够的,因为非常糟糕的消费代码总是可以显式指定扩展Response的类型,但如果它不提供相应的transformResponse参数,则挂钩实现无法匹配该任意类型。

因此,我们希望防止在未提供transformResponse时显式指定泛型类型的可能性,以便在这种情况下响应类型将始终为Response 但是如果提供了它,那么我们需要泛型类型(并且如果消费代码仍然试图显式传递一个不匹配的类型,TS 将对其transformResponse值大喊大叫,而不是在钩子默认值上)。

对于这种情况,我们可以使用function 重载,这样在一种情况下没有generics 可以处理,而在另一种情况下,挂钩返回类型与传递的transformResponse回调返回类型相关联:

// A type to simplify the return expression
type HookReturn<ResponseType> =
    | { loading: true }
    | { loading: false; success: true; response: ResponseType }
    | { loading: false; success: false; error: Error }

// Note: omitting the first 2 arguments, since they do not play a role in the issue
function useFetch2(): HookReturn<Response>;
function useFetch2<ResponseType>(transformResponse: (response: Response) => Promise<ResponseType>): HookReturn<ResponseType>;
function useFetch2(transformResponse = (async (response: Response) => response)) {
    return { // Dummy implementation
        loading: true
    }
}

让我们检查一下它的用法:

// Form with no transformResponse, defaults to async identity, and return type to Response
const a = useFetch2() // Okay
//    ^? HookReturn<Response>

// Form with no transformResponse, but trying to specify a generic type
const b = useFetch2<number>() // Error: Expected 1 arguments, but got 0.
//        ~~~~~~~~~~~~~~~~~~~
//    ^? HookReturn<Response>

// Form with provided transformResponse, return type is inferred from the callback return
const c = useFetch2(async () => true) // Okay
//    ^? HookReturn<boolean>

// Form with provided transformResponse, explicitly specifying a mismatching generic type
const d = useFetch2<number>(async () => true) // Error: Type 'boolean' is not assignable to type 'number'.
//                                      ~~~~
//    ^? HookReturn<number>

游乐场链接

您为transformResponse提供的默认 function 返回Promise<Response>而不是Promise<ResponseType>

您可能可以强制转换响应并将默认类型添加到 ReponseType = Response

const useFetch = <ResponseType = Response>(
  fetchInputFactory: () => RequestInfo | URL | [RequestInfo | URL, RequestInit],
  deps: DependencyList | undefined,
  transformResponse: (response: Response) => Promise<ResponseType> = async (response) => response as ResponseType
):
  | { loading: true }
  | { loading: false; success: true; response: ResponseType }
  | { loading: false; success: false; error: Error }

我猜这更好地描述了默认行为

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM