reactjs - 函数的类型推断作为 Typescript 中的参数
问题描述
我的情况如下图所示 - 我的函数接受对象形式的选项,其中一个参数是transform
函数。此函数接受response
为整个函数正确计算类型的参数(第一张和第二张图片),但我的打字稿编译器将response
参数隐式视为any
类型(第三张图片)。我不明白为什么它不能正确地假设在这种情况下response
应该是正确的类型ApiResponse<NewsListApiResponseData>
?
Error:(27, 20) TS7006: Parameter 'response' implicitly has an 'any' type.
这是我在这种情况下的重载useAxios
钩子定义:
export function useAxios<ResponseData, TransformedData = false | null | ResponseData>(
endpoint: string,
options: {
autoStart?: boolean;
transform: (response: ApiResponse<ResponseData>) => TransformedData;
onResolve?: (data: TransformedData) => void;
onReject?: (error: Error) => void;
} & AxiosHookRequestConfig
): {
loading: boolean;
canceled: boolean;
error?: Error;
response?: AxiosResponse<ApiResponse<ResponseData>>;
data?: TransformedData;
request: (config?: AxiosHookRequestConfig) => Promise<TransformedData>;
cancel: (reason?: string) => void;
};
编辑:添加AxiosHookRequestConfig
定义。
export interface AxiosHookRequestConfig extends Omit<AxiosRequestConfig, 'url' | 'cancelToken'> {
page?: number;
lang?: string;
cache?: boolean | string;
}
export interface AxiosRequestConfig {
url?: string;
method?: Method;
baseURL?: string;
transformRequest?: AxiosTransformer | AxiosTransformer[];
transformResponse?: AxiosTransformer | AxiosTransformer[];
headers?: any;
params?: any;
paramsSerializer?: (params: any) => string;
data?: any;
timeout?: number;
withCredentials?: boolean;
adapter?: AxiosAdapter;
auth?: AxiosBasicCredentials;
responseType?: ResponseType;
xsrfCookieName?: string;
xsrfHeaderName?: string;
onUploadProgress?: (progressEvent: any) => void;
onDownloadProgress?: (progressEvent: any) => void;
maxContentLength?: number;
validateStatus?: (status: number) => boolean;
maxRedirects?: number;
socketPath?: string | null;
httpAgent?: any;
httpsAgent?: any;
proxy?: AxiosProxyConfig | false;
cancelToken?: CancelToken;
}
编辑2:示例
解决方案
让我们看看我认为这个问题的本质是什么:
declare function foo<T>(x: { prop: T }): void;
declare function foo<T>(x: { prop: T, func: (x: T) => void }): void;
foo({ prop: "hey", func: x => x.length }); // error!
// ┌─────────────────> ~
// Parameter 'x' implicitly has an 'any' type.
情况与此报告的问题相同,标记为“按预期工作”。问题是调用匹配第一个重载签名,因此编译器无法弄清楚如何推断x
.
它如何匹配两个签名?答案是 TypeScript 中的对象类型并不精确。如果你有一个类似的接口interface Foo {a: string}
,和一个接口interface Bar extends Foo {b: string}
,那么每个实例Bar
也是一个子类型是一个事实Foo
。这意味着,一般来说,like 类型{a: string}
与其中具有额外属性的任何对象兼容,只要它具有名为的字符串值属性a
。通常编译器会通过警告过多的属性来帮助人们不犯错误,但这些检查似乎不会在这里发生,因为func
它是它正在检查的类型之一。不知道为什么,也许这是一个设计限制或错误。
无论如何,编译器将func
其视为某种函数类型,但由于它匹配第一个重载,因此上下文类型 forx
不起作用,并且您会得到“隐式任何”错误。
有几种方法可以在这里进行。一个是切换重载的顺序,使第一个重载更具限制性。顶部的抓包过载签名最终会阻止过载解决方案通过它。一般来说,这是一个很好的规则:顶部有更具体的重载,底部有更一般的重载:
declare function foo<T>(x: { prop: T, func: (x: T) => void }): void;
declare function foo<T>(x: { prop: T }): void;
foo({ prop: "hey", func: x => x.length }); // okay
这选择了第一个重载,并且类型func
是已知的,并被x
推断为string
.
另一种进行的方法是采用更一般的重载并对其进行更改,以便它实际上禁止相关调用,方法是创建func
一个它不能拥有的属性,如下所示:
declare function foo<T>(x: { prop: T, func?: never }): void;
declare function foo<T>(x: { prop: T, func: (x: T) => void }): void;
foo({ prop: "hey", func: x => x.length }); // okay
这现在有效,因为在第一个重载中,func
是一个可选属性,其值为 type never
。undefined
除了满足这一点,别无他法,而像这样的功能x => x.length
肯定不会。所以调用会跳过第一个重载,选择第二个,然后推断string
.x
最后,在所讨论的两个重载非常相似的情况下,除了可能存在的属性,我倾向于将它们折叠成一个签名并完全忘记重载。这可能与您的用例不匹配,但请记住:
declare function foo<T>(x: { prop: T, func?: (x: T) => void }): void;
foo({ prop: "hey", func: x => x.length }); // okay
现在只有一个呼叫签名,func
可以存在也可以不存在。
其中之一应该有望为您工作。 我已经在你的例子中测试了第二个,它的价值。
好的,祝你好运!
推荐阅读
- c++ - 从 std::unique_ptr 数组中获取原始指针数组
- java - Java将file.txt中的多行数据插入数据库表
- javascript - 无法推入多维数组
- python - 套接字不断返回双字符串值
- javascript - 在按钮单击 Angular 上更新列表
- powershell - 如何使用powershell删除所有临时文件
- mongodb - MongoDB TTL,但要做其他事情
- sql - 如何将 1 列中的值分隔为 count 大于 1 的 2 个单独列。不希望行值在列名中
- f# - 计算表达式中的类型推断错误
- perl - 在 Strawberry Perl 中安装“autovivification”模块时出现问题