包装任意功能时,如何保留类型信息



我正在尝试使用带有ES6承诺的TypeScript实现蓝鸟的Promise.method等效。

所需用法:

const stringify = promiseMethod(JSON.stringify)
stringify(/* ... */) //Type checking available here, returns Promise<string>

最近的实现:

const promiseMethod = function<T, U> (fn: (T) => U) {
    if (typeof fn !== "function") {
        throw new TypeError("Parameter is not a function:" + fn);
    }
    return <(T) => Promise<U>>function () {
        try{
          var value = fn.apply(this, arguments);
          return Promise.resolve(value);
        }
        catch (error){
          return Promise.reject(error);
        }
    };
};

上述实现的问题是呼叫站点期望有很多参数。

如果我将参数和返回类型更改为Function,则可以获取可编译代码,但是对于参数或返回类型没有类型信息。

没有办法以100%类型的方式执行此操作(Afaik!请指出我错过了什么。(

在一个完美的世界中,打字稿将支持使用类型参数来支持整个参数列表。因此,您可以这样做:

function promiseMethod<T,R>(fn: (...args: T) => R) {

但这是不允许的。您能做的最好的是(...args: Array<any>),这很la脚。

(有关此功能有一些讨论,请参见此处和此处的GitHub问题。(


您可以采用大锤方法并超载promiseMethod功能,例如:

function promiseMethod<R>(fn: () => R): () => Promise<R>;
function promiseMethod<R,A>(fn: (a: A) => R): (a: A) => Promise<R>;
function promiseMethod<R,A,B>(fn: (a: A, b: B) => R): (a: A, b: B) => Promise<R>;
// etc...
function promiseMethod<R>(fn: (...args: Array<any>) => R) {
  // implementation
}

这可能满足您的需求,但确实有一些问题:

  • 您仍然可以用比您的巨型超额载荷更多地调用promiseMethod,而在这种情况下的打字将是漏洞的。
  • 如果fn本身有过载,也漏水(请参见下文(。

仍然比...args: Array<any>好。许多常用的库(例如lodash(使用此模式,因为缺乏任何更好的替代方案。


如果要传递的功能有超载...祝您好运。打字稿对功能超载的支持很浅(似乎是设计(。如果您在不调用它的情况下参考过载函数(例如,通过将其传递到promiseMethod函数作为回调(,则编译器似乎仅使用定义过载的最后(?(的类型签名,然后将其扔掉。不太好。当然,这只会在实际传递超载功能时才会咬您。


最后,我的看法。除非您将大型JS代码库升级到Typescript,否则如果可能的话,我将完全避免避免承诺模式。尤其是现在完全支持async/await(由于TS 2.1(,我认为没有用。

最新更新