TypeScript获取内部映射的正确`keyof`

时间:2019-01-08 12:53:52

标签: typescript

这是我的代码

const functions={
    top1: {
        f1: () => 'string',
        f2: (b: boolean, n: number) => 1
    },
    top2: {
        f3: (b: boolean) => b
    }
}

我想创建一个apply函数,如下所示:

function apply (top: keyof typeof functions, functionName: string, inputs: any[]) {
    return functions[top][functionName](...inputs)
}

以便我可以console.log以下值

console.log(apply('top1', 'f1', [])); // 'string'
console.log(apply('top1', 'f2', [true, 23])); // 1
console.log(apply('top2', 'f3', [false])); // false
apply('top2', 'f3', [1]); // show throw TS error

但是,在--strict模式下,我遇到以下错误:

“元素隐式具有'any'类型,因为类型'...'没有索引签名”

这是可以理解的,因为functionName是字符串,而不是keyof typeof functions[section]。我该如何工作?

1 个答案:

答案 0 :(得分:1)

您需要使用通用类型参数来捕获传递给函数的具体键。如果这样做,打字稿将允许您编制索引。

您还想使用一些条件类型来提取参数类型和返回类型,以使传入的参数和返回值类型具有类型安全性

const functions={
    top1: {
        f1: () => 'string',
        f2: (b: boolean, n: number) => 1
    },
    top2: {
        f3: (b: boolean) => b
    }
}

type WeakParameters<T> = T extends (...a: infer A) => any ? A : never;
type WeakReturnType<T> = T extends (...a: any) => infer R ? R : never; 
function apply<KOutter extends keyof (typeof functions),
    KInner extends keyof (typeof functions[KOutter])>(
        top: KOutter, functionName: KInner, inputs: WeakParameters<(typeof functions)[KOutter][KInner]>) : WeakReturnType<(typeof functions)[KOutter][KInner]>{
    var fn = functions[top][functionName]; // OK
    return (fn as unknown as (...a:any[])=> any)(...inputs)
}
console.log(apply('top1', 'f1', [])); // 'string'
console.log(apply('top1', 'f2', [true, 23])); // 1
console.log(apply('top2', 'f3', [false])); // false
apply('top2', 'f3', [1]); // show throw TS error

注意,不幸的是,我们仍然需要使用类型断言。尽管编译器允许我们使用functionstop索引到functionName,但无法确定fn是一个函数,因此我们不能直接调用它(因此类型断言as unknown as (...a:any[])=> any)。同样由于这个原因(因为TS无法确定(typeof functions)[KOutter][KInner]是一个函数),我们不能使用内置的条件类型ParametersReturnType来扩展参数类型和返回类型,我们需要编写自己的版本,这些版本不需要将type参数证明为函数(尽管调用该函数时,一切都会按预期进行)