过滤打字稿中的keyof类型参数

时间:2017-11-17 21:41:27

标签: typescript types

不需要知道React,只是一些背景:在我的用例中,我想用我的自定义高阶组件放大用户指定的React.ComponentClass。为了做到这一点,我希望用户也向我发送我的高阶组件将注入的特定道具的名称列表。这将是这样做的:

function listToComponentProps<TComponentProps, TKey = keyof TComponentProps>(props: Array<TKey>) {
  // Logic here, not important for the example
}
interface TestProps {a: number, b: Function, c: () => number}
listToComponentProps<TestProps>(['a', 'b', 'c'])

keyof关键字为我处理约束。 listToComponentProps<TestProps>的示例输入为

  • 有效:['b']['b', 'c']['c']
  • 无效
    • ['a']['a', 'b']['a', 'b', 'c'](a是数字,不是函数)
    • ['d']['d', 'c'](d不是界面的一部分TestProps

问题是,我想限制props参数不仅是TComponentProps的关键,还要限制TComponentProps中相应类型为Function的关键字(这样'a'将成为typescript编译器检测到的无效选项)。如何才能实现这样的任务?

1 个答案:

答案 0 :(得分:4)

你可以这样做:

const listToComponentProps = <
  TComponent extends {[P in TKey]: Function },
  TKey extends string = keyof TComponent>
  (props: TKey[]) => { /* ... */ };

interface TestProps {a: number, b: Function, c: () => number}
const result = listToComponentProps<TestProps>(['a', 'b', 'c']) // Type error

这会导致类型错误:

Type 'TestProps' does not satisfy the constraint '{ a: Function; b: Function; c: Function; }'.
  Types of property 'a' are incompatible.
    Type 'number' is not assignable to type 'Function'.

不幸的是,拥有默认参数的商家最终会限制我们的TComponent只有Function个属性。如果你真的希望传递listToComponentProps<TestProps>(['b', 'c'])之类的内容,它应该是有效的,你需要明确填写第二个类型参数,即listToComponentProps<TestProps, 'b' | 'c'>(['b', 'c'])

想要的不是TKey的默认参数,而是通用推理是粒度的:在类型参数列表中,可以使用的所有参数被推断(例如TKey,可以从传递的数组中推断出来)应该被推断,即使有些(在这种情况下,TComponent)必须手动指定。 TypeScript今天不会这样工作,所以我们是SOL。

在TypeScript问题跟踪器上有一堆关于此问题的未解决问题,您可以找到它们并开始发臭。

如果好的推理比严格保留运行时特性对你更重要,你可以添加一个伪参数来解释两个类型参数的推理:

const listToComponentProps =
    <TKey extends string>
    (props: TKey[]) =>
    <TComponent extends {[P in TKey]: Function }>
    () => { /* ... */ };

interface TestProps { a: number, b: Function, c: () => number }

const result = listToComponentProps(['a', 'b', 'c'])<TestProps>() // Type error
const result2 = listToComponentProps(['b', 'c'])<TestProps>() // OK