[英]Using Typescript Generics to restrict a functions input parameter
我創建了一個 Typescript package 在我的后端節點 firebase 雲函數和調用它們的前端 React 客戶端之間共享類型。 下面是一些類型的示例。
interface FirstFunctionInput {
x: number
}
interface SecondFunctionInput {
y: string
}
// defines all available functions
enum FirebaseFunction {
FirstFunction = "firstFunction",
SecondFunction = "secondFunction"
}
這就是我目前使用firebase sdk調用函數的方式:
firebase.funtions.httpsCallable('firstFunction')({ x: 21 })
.then(result => { ... })
我的問題是
{ x: 21 }
之類的參數。下面是我(理想情況下)如何看待 API 的示例。 我提供了一個示例,其中向 function 提供了錯誤的輸入類型,並且我希望 Typescript 顯示錯誤。
callFirebaseFunction<FirstFunction>({ y: 'foo' })
^----------^
show error here, since FirstFunction must take a FirstFunctionInput
我已經接近實現這一目標,API 可以工作,但是它需要在調用位置指定輸入參數:
callFirebaseFunction<FirstFunctionInput>(
FirebaseFunction.FirstFunction,
{ x: 21 }
)
.then(result => { ... })
這並不理想,因為程序員需要知道輸入類型才能指定它。
我嘗試了所有不同的擴展接口組合、擴展的抽象類甚至構建器模式,但我找不到將這些組合在一起的方法。 這樣的事情甚至可能嗎?
由於 httpsCallable 中沒有使用泛型,我們應該用httpsCallable
包裝它並使用泛型來限制類型。
這里會用到一些關於Currying的知識,這樣,它的結構就和原來的一樣了。
interface FirstFunctionInput {
x: number
}
interface SecondFunctionInput {
y: string
}
interface FunctionInputs {
firstFunction: FirstFunctionInput,
secondFunction: SecondFunctionInput
}
function callFirebaseFunction<K extends keyof FunctionInputs>(fn: K) {
return function (input: FunctionInputs[K]) {
return firebase.functions().httpsCallable(fn)(input);
};
};
callFirebaseFunction('firstFunction')({ x: 1 }) // OK
callFirebaseFunction('secondFunction')({ y: '1' }) // OK
callFirebaseFunction('test')({ x: 1 }) // ERROR
callFirebaseFunction('secondFunction')({ x: true }) // ERROR
如果要指定output的類型,也就是說
allFirebaseFunction('firstFunction')({ x: 1 }).then((res) => /* specify type of res */)
由於HttpsCallableResult
的類型在源碼中是這樣一個接口:
export interface HttpsCallable {
(data?: any): Promise<{data: any}>;
}
那么擴展{data: any}
的 output 的類型會更好。
最后,我們可以實現如下:
interface FunctionInputs {
firstFunction: {
input: FirstFunctionInput,
output: { data: number }
},
secondFunction: {
input: SecondFunctionInput,
output: { data: boolean }
}
}
function callFirebaseFunction<K extends keyof FunctionInputs>(fn: K) {
return function (input: FunctionInputs[K]['input']): Promise<FunctionInputs[K]['output']> {
return firebase.functions().httpsCallable(fn)(input);
};
};
callFirebaseFunction('firstFunction')({ x: 1 }).then(res => { /* { data: number } */ })
callFirebaseFunction('secondFunction')({ y: '1' }).then(res => {/* { data: boolean } */ })
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.