【问题标题】:Filter on keyof type parameter in typescript过滤打字稿中的keyof类型参数
【发布时间】:2017-11-18 06:36:26
【问题描述】:

无需了解 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&lt;TestProps&gt; 的示例输入为

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

问题是,我想限制props参数不仅是TComponentProps的键,还要限制TComponentProps中对应类型为Function的键(这样'a'就会是打字稿编译器检测到的无效选项)。怎样才能完成这样的任务?

【问题讨论】:

  • 您应该知道用户可以使用此设置执行listToComponentProps&lt;TestProps, "a" | "b" | "c" | "d"&gt;(['a', 'b', 'c', 'd'])TComponentPropsTKey 实际上都没有在这里相互限制。
  • 是的,我知道这一点。但是,高阶组件的用户将无法直接访问该函数,listToComponentProps 将只是一个内部函数。

标签: typescript types


【解决方案1】:

你可以这样做:

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&lt;TestProps&gt;(['b', 'c']) 的东西时,它应该是有效的,你需要显式填写第二个类型参数,即listToComponentProps&lt;TestProps, 'b' | 'c'&gt;(['b', 'c'])

真正想要的不是TKey 的默认参数,而是让泛型推理变得细粒度:在类型参数列表中,可以的所有参数em> 被推断(例如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

【讨论】:

  • 差不多了,我意识到问题写得不是很准确,我还需要它只接受部分接口,例如['b','c']。我已经更新了问题。
  • @netchkin 我明白了,我已经扩展了答案。
  • 哇!只是……哇!谢谢一堆。确实,这个时候,我更看重推理,所以我会使用它。
  • @netchkin 我刚刚意识到虚拟参数不一定需要是咖喱参数。以牺牲一点语法上的笨拙为代价,您可以使用&lt;TKey extends string, TComponent extends ...&gt;(props: TKey[], dummy?: TComponent) =&gt; ...,然后使用listToComponentProps(['b', 'c'], undefined as TestProps)。这消除了运行时影响。
  • 很酷,尽管TComponent 而非props 附加了一个语法错误。
猜你喜欢
  • 2020-08-13
  • 2022-01-21
  • 1970-01-01
  • 2022-08-21
  • 2022-01-18
  • 1970-01-01
  • 2022-09-24
  • 2023-02-16
  • 2018-05-02
相关资源
最近更新 更多