【问题标题】:TypeScript & React: reusable generic actions / componentsTypeScript & React:可重用的通用动作/组件
【发布时间】:2021-02-05 07:16:24
【问题描述】:

我将 TypeScript 与 React 和 useReducer 一起使用,我想以类型安全的方式定义 reducer 操作。

Action 的最简单近似是:

type Action = {name : string, payload : any}

更精确的版本需要联合类型:

type Action =
  | {name : "setColumns", payload: string[]}
  | {name : "toggleColumn", payload: string}
  ...

到目前为止一切顺利。然后我想定义依赖于 Action 或者更确切地说是它的派生词React.Dispatch<Action> 的组件。有两种方法可以做到这一点:

  1. 接受(多个)泛型
  2. 定义更广泛的类型

方法 1) 在理论上更加类型安全,但在实践中更加冗长和复杂。 方法 2) 可以很好地平衡安全性和复杂性。

Pager 两种样式的组件道具示例:

// 1)
export type PagerProps1 <Page extends number, Limit extends number> = {
  page : Page // -- narrower types
  limit : Limit
  pagesTotal : number
}

// 2)
export type PagerProps2 = {
  page : number // -- wider types
  limit : number
  pagesTotal : number
}

^ 现在可以定义 Pager2 并将其移动到库中,而不依赖于特定于应用程序的 PageLimit。并且没有泛型。这是提供必要背景的介绍。

问题来自React.Dispatch。下面是在存在更精确版本的地方模仿通用调度重用的测试用例:

type Action =
  | {name : "setColumn"}
  | {name : "toggleColumn"}

type OpaqueAction1 = {name : any}    // will work
type OpaqueAction2 = {name : string} // will not work

type Dispatch = React.Dispatch<Action>
type OpaqueDispatch1 = React.Dispatch<OpaqueAction1> // will work
type OpaqueDispatch2 = React.Dispatch<OpaqueAction2> // will not work

export const DemoComponent = () => {
  const dispatch = React.useReducer(() => null, null)[1]
  const d0 : Dispatch = dispatch
  const d1 : OpaqueDispatch1 = d0 // ok
  const d2 : OpaqueDispatch2 = d0 // type error
}

错误如下:

TS2322: Type 'Dispatch<Action>' is not assignable to type 'Dispatch<OpaqueAction2>'.   
Type 'OpaqueAction2' is not assignable to type 'Action'.     
Type 'OpaqueAction2' is not assignable to type '{ name: "toggleColumn"; }'.       
Types of property 'name' are incompatible.         
Type 'string' is not assignable to type '"toggleColumn"'.

^ 但在上面的代码中,我们实际上将"toggleColumn" 分配给了string。出了点问题。

这里是沙盒:https://codesandbox.io/s/crazy-butterfly-yldoq?file=/src/App.tsx:504-544

【问题讨论】:

  • 当你处理一个像 dispatch 这样的函数时,一个接受更广泛的参数类型集合的函数扩展了一个接受更窄集合的函数。

标签: reactjs typescript react-hooks use-reducer redux-reducers


【解决方案1】:

您不是将"toggleColumn" 分配给string,而是将Dispatch&lt;Action&gt; 分配给Dispatch&lt;OpaqueAction2&gt;

问题是Dispatch&lt;Action&gt;是一个函数,只能处理带有name属性"toggleColumn"的参数,而Dispatch&lt;OpaqueAction2&gt;是一个可以处理带有name属性的参数的函数@任意@ 987654330@ 类型。该赋值意味着Dispatch&lt;Action&gt; 也应该能够处理任何string 类型,但它不能。

当且仅当U 可分配给T 时,函数(...args: T) =&gt; R 可分配给(...args: U) =&gt; R。这就是为什么错误消息的前两行颠倒了类型顺序的原因:

Type 'Dispatch<Action>' is not assignable to type 'Dispatch<OpaqueAction2>'.   
Type 'OpaqueAction2' is not assignable to type 'Action'.

【讨论】:

  • 是的,很好。任何对细节感兴趣的人都应该搜索“函数逆变”。
猜你喜欢
  • 2020-10-13
  • 2021-06-17
  • 2019-09-09
  • 2016-11-19
  • 2022-12-11
  • 2021-10-18
  • 2022-08-18
  • 2021-08-31
  • 2021-10-22
相关资源
最近更新 更多