[英]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.