【问题标题】:React: Inject props through HOC without declaring types for themReact:通过 HOC 注入 props 而不为它们声明类型
【发布时间】:2020-08-30 01:57:34
【问题描述】:

我正在尝试在 react-redux 绑定中做一些 connect() 的事情。 这是我在组件中注入 props 的 HOC:

export function withAdditionalProps<T, I>(
  injectedProps: I,
  WrappedComponent: React.ComponentType<T>,
): React.ComponentType<Omit<T, keyof I>> { ... }

如果我声明注入的 props 类型(I 泛型类型),它可以正常工作,但是如果我想做一个 HOC 而不声明这些类型(即时省略注入的 props 键)。如何从传递的道具中确定键? 例如,我尝试过这样的事情:

export function withAdditionalProps<T>(
  injectedProps: { [key: string]: unknown },
  WrappedComponent: React.ComponentType<T>,
): React.ComponentType<Omit<T, keyof typeof injectedProps>> { ... }

const InjectedComponent = withAdditionalProps<AppState>(
  {
     counter: 0
  },
  (props) => (<div>{props.counter}</div>)
);

但它不能正常工作:编译器在渲染组件时抛出错误。看截图(testProp 是一个组件的“原生”prop) 也许任何人都可以帮助我。

【问题讨论】:

  • 我没能得到一个完整的版本,但是你能按照export function withAdditionalProps&lt;T, S extends Record&lt;string, any&gt;&gt;( injectedProps: S, WrappedComponent: React.ComponentType&lt;T&gt;, ): React.ComponentType&lt;Omit&lt;T, keyof S&gt;&gt; { ... } 做点什么吗?
  • @SquattingSlavInTracksuit no,S extends Record&lt;string, any&gt; 这里的问题与我在下面的回答中讨论的{ [key:string]: unknown } 完全相同。它将捕获所有字符串键,因此将省略 T 中的所有内容。使Omit 以当前与 TS 的预期方式一起工作的唯一方法是使用具有声明类型变量的标准泛型。

标签: reactjs typescript


【解决方案1】:

简而言之,您的第二个示例在 Typescript 中是不可能的。

问题是 { [key:string]: unknown } 类型 always 捕获所有可能的字符串作为键,而不是缩小到您在特定调用中使用的具体字符串,这将在此处使用 Omit可能的。

事实上,Omit{ [key:string]: unknown } 一起使用只是省略了所有 个可能的键,因此省略了T 中的所有本机道具。这可能将来可以通过negated types 功能实现,但目前唯一的方法是通过在第一个示例中声明的类型变量。

但是,函数定义中声明的类型变量并不要求您在每次调用时也声明它们。请参见下面的代码 - 编译器将从调用函数时传递的具体参数推断 TI。所以在实践中,这里唯一的区别在于您的 HOC 的类型定义,老实说,无论哪种方式,它似乎都具有相似的工作量/可读性。

function foo<T, I>(t: T, i: I): Omit<T, keyof I> { 
    return { ...t, ...i }
}

// notice explicitly declaring foo<{ a: number, b: number}, { c: number }> 
// is NOT necessary. The correct types are just inferred from the args you pass.
const bar = foo({ a: 1, b: 2 }, { b: 3 })

bar.a // OK
bar.b // ERROR, since b is omitted

【讨论】:

    【解决方案2】:

    如果你在泛型参数中同时捕获组件的Props 类型和注入的props 类型,你可以做到这一点:

    export function withAdditionalProps<C extends unknown, I extends object>(
      injectedProps: I,
      WrappedComponent: React.ComponentType<C>,
    ): React.ComponentType<Omit<C, keyof I>> {
      // ...
    }
    

    不要将AppState 作为通用参数传递,而是确保 TypeScript 有足够的上下文来推断它。您可以通过在包装组件中声明 AppState 来做到这一点:

    interface AppState {
      counter: number;
    }
    
    const InjectedComponent = withAdditionalProps(
      {
        counter: 0,
      },
      (props: AppState) => <div>{props.counter}</div>,
    );
    
    const el = <InjectedComponent />;  // OK
    
    const InjectedComponent2 = withAdditionalProps(
      {
        counter2: 0,
      },
      (props: AppState) => <div>{props.counter}</div>,
    );
    
    const el2 = <InjectedComponent2 />; // error: Property 'counter' is missing
    

    TypeScript won't let you specify some generic parameters but leave others inferred。如果您想通过泛型传递AppState,标准的解决方法是使用两个函数,一个带有显式泛型参数,一个带有推断参数。这是您的示例中的外观:

    export function withAdditionalProps<PropsType>(): <I extends unknown>(
      injectedProps: I,
      WrappedComponent: React.ComponentType<PropsType>,
    ) => React.ComponentType<Omit<PropsType, keyof I>> {
      // ...
    }
    

    这是你如何使用它的:

    interface AppState {
      counter: number;
    }
    const InjectedComponent = withAdditionalProps<AppState>()({
        counter: 0,
      },
      props => <div>{props.counter}</div>,
    );
    
    const el = <InjectedComponent />;  // OK
    
    const InjectedComponent2 = withAdditionalProps<AppState>()(
      {
        counter2: 0,
      },
      props => <div>{props.counter}</div>,
    );
    
    const el2 = <InjectedComponent2 />; // error: Property 'counter' is missing
    

    我无法完全弄清楚您屏幕截图中错误消息背后的原因。如果你可以分享文字或链接,我可以看看。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-08
      • 1970-01-01
      • 2020-10-24
      • 2019-04-25
      • 1970-01-01
      • 2022-01-18
      相关资源
      最近更新 更多