我有两个函数,它们接受一个options
参数,除了用于标识函数的type
之外,它们具有不同的属性。
type FuncAOptions = {
type: 'A'
opt1: string
opt2: boolean
}
function funcA(options: FuncAOptions): number {
if (!options.opt1) throw new Error('Missing required option')
return 1
}
type FuncBOptions = {
type: 'B'
opt3: number
opt4: (a: number) => number
}
function funcB(options: FuncBOptions): string {
if (!options.opt3) throw new Error('Missing required option')
return 'B'
}
然后,我就有了这些函数的Map,以及相关的Map类型,这样我就可以用可变的运行时数据有条件地调用这些函数。
type AllFunctions = FuncAOptions | FuncBOptions
type FunctionMap = { [K in AllFunctions['type']]: (options: any) => any; }
const functionMap: FunctionMap = {
A: funcA,
B: funcB
}
function callFunction(type: keyof typeof functionMap, options: any) {
return functionMap[type](options)
}
当我直接调用函数时,我得到了正确的类型检查,以知道我是否传递了一组不正确的选项。我希望在通过中介方法调用函数时也能做到这一点。
callFunction('A', { type: 'B', opt3: 'Hello' }) // NO TS ERROR
funcA({ type: 'B', opt3: 'Hello' }) // TS ERROR: The expected type comes from property 'type' which is declared here on type 'FuncAOptions'
我喜欢使用K in AllFunctions['type']
类型的Map,因为当我向AllFunctions
添加函数时,我会提醒我需要向functionMap
添加键值对。
完整的例子在这里
1条答案
按热度按时间kkbh8khc1#
如果您希望
functionMap[type](options)
进行类型检查而不使用类型Assert或any
类型,则需要将genericindexes的条件写入基本键值类型或该类型的mapped types。这是在microsoft/TypeScript#47109中描述的。本质上,您希望将
type
视为某种泛型类型K
,将functionMap
视为某种Map类型(如{[P in keyof FuncOptionMap]: (arg: FuncOptionMap[P]) => FuncRetMap[P]}
),将options
视为类型FuncOptionMap[K]
。然后编译器可以得出结论,函数functionMap[type]
的类型为(arg: FuncOptionMap[K]) => FuncRetMap[K]
,因此可以用options
作为参数调用,并将返回相应的返回类型FuncRetMap[K]
。因此,我们需要根据您拥有的值定义FuncOptionMap
和FuncRetMap
。您可能希望您可以在
type
仅仅是等价于keyof FuncOptionMap
的联合类型而不需要泛型的情况下完成此操作。但是TypeScript不能遵循这种逻辑,如microsoft/TypeScript#30581中所述。推荐的方法是使用泛型索引到Map类型中;事实上,microsoft/TypeScript#47109是microsoft/TypeScript#30581的解决方案(或者至少是我们最接近的解决方案)。它可以看起来像这样:
本质上,我们将
functionMap
重命名为_functionMap
,然后将其分配回从它计算出的FuncOptionMap
和FuncRetMap
类型。它看起来可能像一个no-op,因为_functionMap
的类型和functionMap
的类型看起来是相同的。但这其实很重要;编译器只能遵循当事物被写为该Map时的逻辑。如果在下面的代码中尝试使用_functionMap
而不是functionMap
,编译器将丢失线程并输出错误。继续:
该类型检查,返回类型为
FuncRetMap[K]
。现在你得到了预期的错误:当你调用它时,编译器知道输出类型as如何依赖于输入类型:
Playground链接到代码