2017-02-21 51 views
1

我有一個高階函數,例如:如何正確香草JS喜歡這類高階函數

function log(fn) { 
    return (...args) => { 
    console.log("Calling", fn.name); 
    return fn(...args); 
    } 
} 

而像這樣使用:

let name; 
const setName = log((newName) => { 
    name = newName; 
}); 
setName("Hello"); // output "Calling setName" 

我怎樣才能正確地輸入這在TS中如此setName具有正確的功能簽名?含義:

setName("hello") // OK 
setName() // Error, missing arg 
setName(123) // Error, number not string 

我想出到目前爲止是:

function log<F extends Function>(fn: F): F { 
    return (...args: any[]) => { 
    console.log("Calling", fn.name); 
    return fn(...args); 
    } 
} 

let name; 
const setName = log((newName: string) => { 
    name = newName; 
}); 

其中一期工程的方式我想(我得到的參數類型上setName檢查),但我得到的編譯錯誤log函數返回:

Error: TS2322:Type '(...args: any[]) => any' is not assignable to type 'F'.

即使我使用<F extends (...args: any[]) => any>我得到同樣的錯誤。基本上,我不知道如何使返回的函數滿足通用類型F

我該怎麼做?

回答

2

與函數重載

Function overloads可以使用。它的定義從實現分離的方式:我喜歡超負荷的版本,因爲它有助於在模塊分佈爲預編譯的NPM包生產打字稿定義

function log<F extends Function>(fn: F): F 
function log(fn: Function) { 
    return (...args) => { 
     console.log("Calling", fn.name); 
     return fn(...args); 
    } 
} 

與一種類型的斷言as F

類型Function太寬。您的包裝功能必須是以下子類型:(...args: any[]) => any

您可以使用as F爲了說打字稿有信心的恢復功能:

function log<F extends (...args: any[]) => any>(fn: F): F { 
    return ((...args: any[]) => { 
     console.log("Calling", fn.name); 
     return fn(...args); 
    }) as F 
} 

與類型斷言as any

相同的實現,但我們可以用Functionas any代替(...args: any[]) => anyas F(見路易斯的回答)。

+0

謝謝,我沒有想到使用這樣的函數重載。 – Aaron

+0

我編輯與其他貢獻者的想法。 「功能」類型太寬。你的包裝函數必須是:'(... args:any [])=> any'。 – Paleo

+0

有趣的,我試過'F延伸(...args:任何[])=>任何'與我原來的代碼,它仍然不允許我返回,即使有這個確切的簽名出於某種原因,和'作爲F'不能與'F extends Function'一起使用。老實說,我不明白爲什麼這是不同的,但它的工作原理。 – Aaron

2

最簡單的修改就可以使很給力您的返回值的類型any

function log<F extends Function>(fn: F): F { 
    return ((...args: any[]) => { 
    console.log("Calling", fn.name); 
    return fn(...args); 
    }) as any; 
} 

此編譯文件,這樣,您的setName功能將具有相同的簽名爲你傳遞什麼到log

+0

謝謝,我發現這個作品,但我不喜歡使用'作爲任何'...還有,爲什麼不作爲函數'工作? – Aaron

+0

使用'as any'並不比在另一個答案中使用'as F'更糟。無論哪種方式,你都在告訴編譯器「相信我,我知道我在做什麼」。 'as Function'無法工作,因爲'F'由您傳遞的參數決定。到'log'。如果'F'是'Function'的擴展名,那麼將一個'F'類型的對象賦值給一個可以接受類型爲'Function'的對象的變量是非常好的,但是將一個Function類型的對象賦值給一個變量「F」類型的對象不好。如果你使用'as Function'而不是'as'作爲任何',你本質上就是想要做後者。 – Louis

+0

我明白了,謝謝你的解釋。基本上是因爲你不能將一個基類型分配給一個子類型。 – Aaron