2017-06-29 92 views
2

我正在尝试使用带有es6 promise的TypeScript实现等效于bluebird's 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,但没有可用于参数或返回类型的类型信息,则可以获得可编译代码。

+1

TypeScript支持签名重载。一个简单的方法来做到这一点,只是定义多个重载,每个不同数量的参数。 – Wazner

回答

1

有没有办法,在100%的类型安全的方式做到这一点(据我所知!请指出,如果我错过了一些东西。)

在一个完美的世界,打字稿将支持使用类型参数去站在对整个参数列表。所以你可以这样做:

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

但这是不允许的。你可以做的最好的是(...args: Array<any>),这是非常蹩脚的。

(有关于这个功能的一些讨论,请参阅github上发出herehere


你可以采取大锤的方法和重载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)都使用这种模式,因为没有更好的选择。


如果你传递的函数有重载...祝你好运。 Typescript对函数重载的支持很浅(看起来好像是by design)。如果你在不调用它的情况下引用一个重载函数(例如将它作为回调函数传递给你的函数),似乎编译器只是使用最后一个(?)定义的重载的类型签名,并抛弃了其他函数。不太好。当然,如果你真的在传递重载的函数,这只会咬你。


最后,我的意见。除非你将一个大的JS代码库升级到Typescript,否则我会考虑尽可能避免promisifying模式。特别是现在完全支持async/await(自TS 2以来。1),我不认为它有用处。

+0

我不明白你的最终评论。为了使用'async' /'await',我需要'Promise',除非我所有的API都提供了承诺,否则我需要提供一些能够使用'async' /'await'的方法。 – vossad01

+0

我想这取决于你在做什么。但是如果你从异步函数中调用你的方法(你将要传递给'Promise.method'的那些方法),你不必担心包装它们的结果/抛出(这是'Promise.method'的用途,从我看到的) – dbandstra

+0

啊,这很有道理,谢谢阐述!我的情况是不同的,我在异步和非异步API的混合上构建抽象。使一切都返回一个Promise允许呼叫者看到一个通用的接口。 – vossad01

相关问题