不需要知道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编译器检测到的无效选项)。如何才能实现这样的任务?
答案 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