【问题标题】:Can't properly type the following HOC function accepting a custom property key无法正确键入以下接受自定义属性键的 HOC 函数
【发布时间】:2021-10-05 05:00:56
【问题描述】:

我有一个 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 函数返回的组件的组件道具与包装组件的道具链接,但我不知道如何。有什么想法吗?

【问题讨论】:

标签: reactjs typescript typescript-typings


【解决方案1】:

所以问题如下:你有一个带有 3 个类型参数的函数。调用函数时,类型参数被分配实际类型。让我们看看你如何称呼它:

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

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

但是让我们重写一下

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

现在不是那么明显了,因为这些基本上是 2 个独立的调用,彼此无关。你也可以这样做

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

它仍然应该工作。问题是withCustomProperty 的所有 3 种类型参数都在第一行决定,您以后无法更改它们。即使你在一行中写了withCustomProperty(...)(Test),它们仍然是在第一次调用时决定的,并且在你第二次调用它时不能改变Test作为参数。为它们分配了哪些类型? Idk 一些垃圾,可能是 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&lt;P, PropertyKey&gt; 的别名,这样您就不必多次键入它?如果是这样,这是一个非常糟糕的主意,会引起很多头痛。只需在函数中定义一个类型别名,如下所示:

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
  }
}

这消除了第二个错误。老实说,我没有调查到底是什么原因造成的,如果你真的需要第三种类型的参数,请发表评论,我会尝试更新我的答案以适应它。

Sandbox

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-25
    • 1970-01-01
    • 2019-07-05
    • 2011-10-12
    • 1970-01-01
    • 2011-01-19
    • 2016-08-08
    • 2011-09-03
    相关资源
    最近更新 更多