[英]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.