无法正确键入以下接受自定义属性键的 HOC 函数



我有一个HOC函数,它接受一个组件和一个属性键,并返回在指定属性键处注入值的组件。这是我的概念证明:

import * as React from 'react'
type WrappedComponentProps<PropertyKey extends string> = {
[k in PropertyKey]: boolean
}
type WithCustomPropertyProps<P, PropertyKey extends string> = Omit<P, PropertyKey> & P
const withCustomProperty = <
PropertyKey extends string,
P extends WrappedComponentProps<PropertyKey>,
CurrWithCustomPropertyProps = WithCustomPropertyProps<P, PropertyKey>
>(propertyName: PropertyKey) => {
return (WrappedComponent: React.ComponentType<P>): React.FC<CurrWithCustomPropertyProps> => {
const WithMediaQuery: React.FC<CurrWithCustomPropertyProps> = (props: CurrWithCustomPropertyProps) => {
return <WrappedComponent {...props} {...{ [propertyName]: true }} />
}
return WithMediaQuery
}
}
type Props = {
anotherProperty: string
} & WrappedComponentProps<'propertyKey'>
class Test extends React.Component<Props> {
render() {
return <div></div>
}
}
const _test = withCustomProperty('propertyKey')(
Test
)
但是,正如你在TypeScript Playground中看到的,我得到了两个错误:
Type 'CurrWithCustomPropertyProps & { [x: string]: boolean; }' is not assignable to type 'IntrinsicAttributes & P & { children?: ReactNode; }'.
Type 'CurrWithCustomPropertyProps & { [x: string]: boolean; }' is not assignable to type 'P'.
'P' could be instantiated with an arbitrary type which could be unrelated to 'CurrWithCustomPropertyProps & { [x: string]: boolean; }'.
Argument of type 'typeof Test' is not assignable to parameter of type 'ComponentType<WrappedComponentProps<"propertyKey">>'.
Type 'typeof Test' is not assignable to type 'ComponentClass<WrappedComponentProps<"propertyKey">, any>'.
Types of parameters 'props' and 'props' are incompatible.
Type 'WrappedComponentProps<"propertyKey">' is not assignable to type 'TestProps | Readonly<TestProps>'.
Property 'anotherProperty' is missing in type 'WrappedComponentProps<"propertyKey">' but required in type 'Readonly<TestProps>'.
我承认我对这里发生的事情没有任何线索。以上是我尝试过的替代方案之一,我尝试了其他几种,但都没有任何改善。

我想我应该将HOC函数返回的组件的组件道具与包装组件的组件道具链接起来,但我不知道怎么做。什么好主意吗?

问题如下:你有一个函数有3个类型参数。类型参数在调用函数时被分配实际类型。让我们看看你是怎么称呼它的:

const _test = withCustomProperty('propertyKey')(Test)

这里你可能认为很明显,PropertyKey应该是"propertyKey",P应该是TestProps,第三个参数应该是它应该是什么,我没有大脑能力来计算它是什么。

我们重写一下

const hoc = withCustomProperty('propertyKey')
const _test = hoc(Test)

现在它不是那么明显,因为这基本上是两个独立的调用,彼此没有任何关系。你也可以这样做

const hoc = withCustomProperty('propertyKey')
const _test = hoc(Test)
const _test1 = hoc(SomeOtherComponentWithDifferentProps)

它仍然应该工作。问题是withCustomProperty的所有3个类型参数都是在第一行决定的,以后不能更改它们。即使您在一行中写入withCustomProperty(...)(Test),它们仍然都是在第一次调用期间确定的,并且当您以Test作为参数第二次调用时不能更改。它们被分配了什么类型?我想要一些垃圾,可能是never或其他东西

因此,您需要做的是在调用withCustomProperty(即PropertyKey)时仅确定一个类型参数,而在调用Test组件返回的任何类型参数时确定其余类型参数。所以你的withCustomProperty泛型函数应该只接受一个类型参数,这将在它的调用中决定,它应该返回另一个泛型函数,它有剩下的参数稍后决定。如下图所示:

const withCustomProperty = <
// Only one type argument here
PropertyKey extends string
>(propertyName: PropertyKey) => {
return <
// We return a generic function and the rest of type arguments go here
P extends WrappedComponentProps<PropertyKey>,
CurrWithCustomPropertyProps = WithCustomPropertyProps<P, PropertyKey>
>(WrappedComponent: React.ComponentType<P>): React.FC<CurrWithCustomPropertyProps> => {
const WithMediaQuery: React.FC<CurrWithCustomPropertyProps> = (props: CurrWithCustomPropertyProps) => {
return <WrappedComponent {...props} {...{ [propertyName]: true }} />
}
return WithMediaQuery
}
}

这就解决了错误,差不多就是这样了。

我也不确定这第三个参数CurrWithCustomPropertyProps的目的是什么。你只是使用它作为WithCustomPropertyProps<P, PropertyKey>的别名,这样你就不必多次输入它?如果是这样,这是一个非常糟糕的主意,会引起很多头痛。只需在函数中定义一个类型别名,如下所示:

const withCustomProperty = <
PropertyKey extends string
>(propertyName: PropertyKey) => {
return <
P extends WrappedComponentProps<PropertyKey>
>(WrappedComponent: React.ComponentType<P>): React.FC<
// You have to write the whole thing here, because type
// aliases can only be declared in blocks
WithCustomPropertyProps<P, PropertyKey>
> => {
// Define type alias here
type RealProps = WithCustomPropertyProps<P, PropertyKey>
const WithMediaQuery: React.FC<RealProps> = (props: RealProps) => {
return <WrappedComponent {...props} {...{ [propertyName]: true }} />
}
return WithMediaQuery
}
}

这消除了第二个错误。我没有研究到底是什么导致了它,老实说,如果你真的需要第三种类型的参数,请留下评论,我将尝试更新我的答案以适应它。

沙箱

最新更新