繁体   English   中英

function 的类型推断作为 Typescript 中的参数

[英]Type inference of function as argument in Typescript

我的案例如下图所示 - 我的 function 接受 object 形式的选项,其中一个 arguments 正在transform ZC1C42145268E683985DZ 此 function 接受response参数,该参数是为整个 function 正确计算的类型(第一张和第二张图片),但我的 typescript 编译器将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 中的 object 类型不准确 如果你有一个像interface Foo {a: string}这样的接口,并且一个接口interface Bar extends Foo {b: string} ,那么每个Bar的实例也是一个Foo是子类型化的事实。 这意味着,一般来说,像{a: string}这样的类型与任何具有额外属性的 object 兼容,只要它具有名为a的字符串值属性。 编译器通常会通过警告过多的属性来帮助人们不犯错误,但这些检查似乎不会在这里发生,因为func是它正在检查的类型之一。 不知道为什么,也许这是一个设计限制或错误。

无论如何,编译器将func视为某种 function 类型,但由于它与第一个重载匹配,因此x的上下文类型不起作用,并且您会收到“隐式任何”错误。


有几种方法可以在这里进行。 一个是切换重载的顺序,使第一个重载更具限制性。 顶部的抓包过载签名最终会阻止过载解决方案通过它。 一般来说,这是一个很好的规则:顶部有更具体的重载,底部有更一般的重载:

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是一个可选属性,其值为never类型。 除了undefined之外没有其他方法可以满足这一点,而像x => x.length这样的 function 肯定不会。 所以调用跳过第一个重载,选择第二个,并推断xstring


最后,如果所讨论的两个重载非常相似,除了可能存在的属性,我倾向于将它们折叠成一个签名并完全忘记重载。 这可能与您的用例不匹配,但请记住:

declare function foo<T>(x: { prop: T, func?: (x: T) => void }): void;
foo({ prop: "hey", func: x => x.length }); // okay

现在只有一个调用签名,并且func可以存在或不存在。


其中之一应该有望为您工作。 我已经在你的例子中测试了第二个,它的价值。

好的,祝你好运!

链接到代码

暂无
暂无

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

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