TypeScript重载useFetch钩子无法推导ErrType泛型类型的问题
问题:TypeScript无法推导useFetch重载中的ErrType泛型类型
我正在实现一个带可选错误格式化器的重载useFetch钩子,错误格式化器可以返回任意类型,因此将其设为泛型类型。但TypeScript无法从formatError参数函数中推导ErrType的类型。我尝试了一个简化示例,该示例可以正常工作,却不明白useFetch为何不行,希望了解原因及解决方法。
useFetch代码实现
type UseFetchReturnType<ResponseType, ErrType> = { data: ResponseType | null, error: ErrType | null, loading: boolean }; function useFecth<ResponseType>(url: string): UseFetchReturnType<ResponseType, string>; function useFecth< ResponseType, ErrType, >(url: string, formatError: (x: any) => ErrType): UseFetchReturnType<ResponseType, ErrType>; function useFecth<ResponseType>(url: string, formatError?: (x: any) => unknown) { ... ... ... return { data, error, loading, }; } const { data: numArr, error: numberErr } = useFecth<number[]>('https://url1', (x: any): number => 7); const { data: stringVal, error: boolErr } = useFecth<string>('https://url2', (x: any): boolean => false); const { data: stringArr, error: stringErr } = useFecth<string[]>('https://url2');
可正常工作的简化示例
// 泛型作为函数返回类型,可正常推导 function fnWrapper(): string; function fnWrapper<T>(callback: () => T): T; function fnWrapper(cb?: () => unknown) { return cb !== undefined ? cb() : 'default value'; } const a = fnWrapper(() => 7); const b = fnWrapper(() => 'String val'); const c = fnWrapper();
问题原因
核心问题是重载签名与实现签名的泛型不匹配:
- useFetch的第二个重载签名需要
ResponseType和ErrType两个泛型,但实现签名只声明了ResponseType,缺失ErrType泛型参数。TypeScript处理重载时会优先匹配重载签名,但实现层的泛型缺失导致无法从formatError参数推导ErrType。 - 对比简化示例
fnWrapper:它的实现签名没有显式声明泛型,TypeScript可以直接从重载的回调参数中自动推导T类型;而useFetch的实现签名仅指定了ResponseType,无法关联到ErrType的推导逻辑。
解决方法
修改实现签名,将ErrType纳入泛型参数并设置默认值为string,同时修正函数名拼写(原代码中useFecth拼写错误,应为useFetch):
type UseFetchReturnType<ResponseType, ErrType> = { data: ResponseType | null, error: ErrType | null, loading: boolean }; function useFetch<ResponseType>(url: string): UseFetchReturnType<ResponseType, string>; function useFetch<ResponseType, ErrType>(url: string, formatError: (x: any) => ErrType): UseFetchReturnType<ResponseType, ErrType>; // 实现签名添加ErrType泛型,并设置默认值兼容无formatError的场景 function useFetch<ResponseType, ErrType = string>(url: string, formatError?: (x: any) => ErrType) { // 保留原有业务逻辑 const data: ResponseType | null = null; const error: ErrType | null = null; const loading = false; return { data, error, loading, }; } // 现在类型推导正常工作 const { data: numArr, error: numberErr } = useFetch<number[]>('https://url1', (x: any): number => 7); const { data: stringVal, error: boolErr } = useFetch<string>('https://url2', (x: any): boolean => false); const { data: stringArr, error: stringErr } = useFetch<string[]>('https://url2');
说明
- 给
ErrType设置默认值string后,当不传递formatError参数时,会自动匹配第一个重载的返回类型(错误类型为string)。 - 实现签名包含两个泛型参数,TypeScript可以从
formatError的返回值中正确推导ErrType,同时兼容两种调用场景。
内容的提问来源于stack exchange,提问作者Žilvinas Vidmantas
相关产品推荐
相关产品推荐

