将我的泛型应用于TypeScript定义时遇到问题。
定义如下:
export function readFile(path: PathLike | number, options: { encoding?: null; flag?: string; } | undefined | null, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void;
export function readFile(path: PathLike | number, options: { encoding: string; flag?: string; } | string, callback: (err: NodeJS.ErrnoException, data: string) => void): void;
export function readFile(path: PathLike | number, options: { encoding?: string | null; flag?: string; } | string | undefined | null, callback: (err: NodeJS.ErrnoException, data: string | Buffer) => void): void;
export function readFile(path: PathLike | number, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void;
正如您所看到的,他们接受2到3个参数,最后一个参数总是回调。
现在我的通用是:
function cbCall<Ret, Arg1, Arg2>(
fun: (arg1: Arg1, arg2: Arg2, cb: (error: any, result: Ret) => any) => any,
obj: any,
arg1: Arg1,
arg2: Arg2
): Context<Ret>;
因为它期望一个带有3个参数的函数(最后一个是回调),我猜它会匹配前3个定义中的一个。但是,当我尝试使用cbCall
时这样:
CL.cbCall(
fs.readFile,
fs,
'filename',
{ encoding: 'utf-8' }
);
我收到错误:
Argument of type '{ encoding: string; }' is not assignable to parameter of type '(err: ErrnoException, data: Buffer) => void'.
所以它以某种方式预期arg2
应该是cb
应该是什么。
如果我删除Arg2
模板参数并将其替换为object
,则可以使用,但这对我的用例来说不够通用。
请解释为什么会这样,以及我是否能达到我想要实现的目标。
编辑:使用cbCall<string, string, object>(...)
也可以,但是再次失败了。
编辑:我将示例简化为:
function fun(arg: string, cb: (err: string, res: string) => void);
function fun(cb: (err: string, res: string) => void): void;
function fun(arg: any, cb?: any): void {
// noop
}
function call<Ret, Arg>(fun: (arg: Arg, cb: (err: string, res: Ret) => void) => any, arg: Arg) {
// noop
}
// this works
call<string, string>(fun,'test');
// Argument of type '"test"' is not assignable to parameter of type '(err: string, res: string) => void'.
call(fun,'test'); //
更改前两行的顺序也可以修复它。 但是不应该使用第一个匹配声明而不是最后一个吗?
答案 0 :(得分:3)
类型参数分配在重载解决之前进行,并且不会在故障时回溯,因此有时您会遇到可以通过选择不同的重载来避免的错误。我还没有找到类型参数匹配算法的一个很好的解释,但这个issue解释了一下。
由于您的fun
有一个重载,readFile
的最后一次重载将被选中,因为它被认为是最不具体的重载。作为类型错误的解决方法,您可以在调用cbCall
的模块中自己提供所需的重载:
declare module 'fs' {
function readFile(path: fs.PathLike | number, options: { encoding?: string | null; flag?: string; } | string | undefined | null, callback: (err: NodeJS.ErrnoException, data: string | Buffer) => void): void;
}
但是,它仍然不是很好。在崩溃类型检查器(issue)之后,我自己对泛型变得更加保守了。在一个版本中工作的复杂解决方案通常会在下一个版本中解决,并且有很多未解决的问题没有取得多大进展。希望将来会更稳定。
编辑:想到另一种选择。如果传递给cbCall
的回调函数都没有options
参数共享最后一个重载,那么您也可以将该重载添加到fun
的类型中:
interface CallbackFun<Arg1, Arg2, Ret> {
(arg1 : Arg1, arg2 : Arg2, cb : (error : any, result : Ret) => any) : any
(arg1 : Arg1, cb : (error : any, result : Ret) => any) : any
}
并在fun: CallbackFun<Arg1, Arg2, Ret>
的定义中使用cbCall
。