簡體   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