0

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

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

4

1 回答 1

2

您不是分配"toggleColumn"string,而是分配Dispatch<Action>Dispatch<OpaqueAction2>

问题在于,这Dispatch<Action>是一个只能处理具有name属性的参数的函数"toggleColumn"Dispatch<OpaqueAction2>而是一个可以处理具有name任何string类型的属性的参数的函数。赋值意味着它Dispatch<Action>也应该能够处理任何string类型,但它不能。

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

Type 'Dispatch<Action>' is not assignable to type 'Dispatch<OpaqueAction2>'.   
Type 'OpaqueAction2' is not assignable to type 'Action'.
于 2021-02-05T07:49:09.173 回答