假设我有一个日志记录函数,该函数接受一个函数并记录名称,参数和结果:
function log<A extends any[], B>(f: (...args: A) => B): (...args: A) => B {
return function (...args: A): B {
console.log(f.name);
console.log(JSON.stringify(args));
const result = f(...args);
console.log(result);
return result;
}
}
这有效,并且AFAICT保留了传入函数的类型安全性。但这会中断,如果我想为Promises添加特殊处理:
function log<A extends any[], B>(f: (...args: A) => B) {
return function (...args: A): B {
console.log(f.name);
console.log(JSON.stringify(args));
const result = f(...args);
if (result && typeof result.then === 'function') {
result.then(console.log).catch(console.error);
} else {
console.log(result);
}
return result;
}
}
在这里,编译器抱怨.then
在类型B上不存在。所以我可以转换为Promise:
if (typeof (<Promise<any>>result).then === 'function') {
这也不起作用,它是比通用类型更具体的类型。错误消息建议转换为未知:
const result: unknown = f(...args);
但是现在返回的类型与HOF的返回签名不匹配,并且编译器自然不允许这样做。
现在,我可以使用instanceof Promise
的支票了:
if (result instanceof Promise) {
result.then(console.log).catch(console.error);
并且编译器很高兴。但是,这并不理想:我更愿意对所有可能的模型进行通用测试,而不仅仅是对本地Promise构造函数进行通用测试(更不用说像Promise构造函数之类的奇怪场景来自不同窗口)了。我也希望这是一个功能,而不是两个(或更多!)。实际上,使用此检查来确定对象上是否存在方法是一种非常常见的Javascript习惯用法。
如何在保留原始函数参数的返回类型的同时执行此操作?
答案 0 :(得分:2)
我更愿意对所有可能的模型进行通用测试,而不仅仅是对本地Promise构造函数进行测试
这可能满足您的要求:
if (result && typeof (result as any).then === 'function') {
(result as any).then(console.log).catch(console.error);
} else {
console.log(result);
}
如果这样做,则可以将其into a user-defined type guard分解:
const isThenable = (input: any): input is Promise<any> => {
return input && typeof input.then === 'function';
}
使用用户定义的类型防护,log
函数将看起来像这样(here it is in the TypeScript playground):
const isThenable = (input: any): input is Promise<any> => {
return input && typeof input.then === 'function';
}
function log<A extends any[], B>(f: (...args: A) => B) {
return function (...args: A): B {
console.log(f.name);
console.log(JSON.stringify(args));
const result = f(...args);
if (isThenable(result)) {
result.then(console.log).catch(console.error);
} else {
console.log(result);
}
return result;
}
}