2017-06-29 91 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

相關問題