我的情况如下图所示-我的函数接受对象形式的选项,其中一个参数为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;
}
Edit2:Example
答案 0 :(得分:1)
让我们看看我认为这个问题的实质:
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.
情况与optimizer中的情况相同,标记为“按预期工作”。问题在于该调用与第一个重载签名匹配,因此编译器无法弄清楚如何推断x
的类型。
它如何匹配两个签名?答案是TypeScript中的对象类型不是this reported issue。如果您有一个类似interface Foo {a: string}
的接口和一个interface Bar extends Foo {b: string}
的接口,这是子类型化的事实,即Bar
的每个实例也是一个Foo
。这意味着,一般来说,像{a: string}
这样的类型与其中具有附加属性的任何对象都兼容,只要它具有名为a
的字符串值的属性即可。通常,编译器会尝试通过exact来帮助人们不要犯错误,但是由于func
属于它正在检查的一种类型,因此这些检查似乎没有在这里进行。不知道为什么,也许这是设计限制或错误。
无论如何,编译器将func
视为某种函数类型,但是由于它与第一个重载匹配,因此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
之类的函数肯定不满足。因此,该调用将跳过第一个重载,选择第二个重载,并为string
推断x
。
最后,在所讨论的两个重载非常相似的情况下,除了可能存在的属性外,我倾向于将它们折叠为单个签名,而完全忘记了重载。这可能与您的用例不符,但请记住以下几点:
declare function foo<T>(x: { prop: T, func?: (x: T) => void }): void;
foo({ prop: "hey", func: x => x.length }); // okay
现在只有一个呼叫签名,并且func
可以存在或不存在。
其中之一有望为您工作。 warning about excess properties,物有所值。
好的,祝你好运!