【问题标题】:Typescript, tagged actions and array of reducers打字稿,标记的动作和减速器数组
【发布时间】:2019-01-17 00:03:05
【问题描述】:

我正在使用 typescript 并编写大量借鉴 redux 工作方式的代码,(action, state) => state

非常努力地以最严格的方式使用打字稿。我的问题最好用这个例子来解释。

首先,我有一个描述动作类型的枚举:

enum Tags {
  TAG_A = 'tagA',
  TAG_B = 'tagB'
}

接下来我定义每种类型的唯一字段:

type Payload = {
  [Tags.TAG_A]: {
    foo: string;
  },
  [Tags.TAG_B]: {
    bar: number;
  }
}

现在我定义我的动作类型:

type Action<T extends Tags> = {
  type: T;
  id: string;
  moreCommonField: boolean;
} & Payload[T];

type Actions = { [T in Tags]: Action<T> }[Tags]

现在是减速器:

type State = { somevar: string }; //doesnt matter, example

const reducer = (action: Actions, state: State): State => {
  switch (action.type) {
    case Tags.TAG_A:
      action.foo; /* correct type, yay */
      break;
    case Tags.TAG_B:
      action.bar; /* correct type, yay */
      break;
  }
  return {...state};
}

到目前为止,一切都很好!

但在现实世界中,我有很多很多动作,我的switch 看起来有点丑。

所以我尝试了这个:

const minireducers: { [K in Tags]: (action: Action<K>, state: State) => State } = {
  [Tags.TAG_A]: (action, state) => {
    // we have correct type of action here, big benefit
    return {...state};
  },
  [Tags.TAG_B]: (action, state) => ({...state})
}

这很好,因为大多数“微型减速器”只是单线器。这还有一个好处是迫使我不要忘记处理我以后可能添加的任何操作。

但是,当我尝试为此编写实际的减速器时:

const reducer2 = (action: Actions, state: State) => {
  const minireducer = minireducers[action.type]; 
  const newState = minireducer(action, state); //error
  return newState;
}

这里的错误我很清楚,action.type 没有缩小到任何特定类型,所以minireducer 可以是很多东西,它的类型是联合,你不能调用联合。使用铸造它显然会起作用,但我想要一个不需要铸造的解决方案。甚至只是一个思想实验。

任何人对尽可能类型安全的解决方案有任何想法,这将允许我将减速器拆分为示例中的较小减速器?

您不必拘泥于我的类型定义或结构,但同样,类型安全和良好的类型推断是这里的目标。

link to playground

【问题讨论】:

    标签: typescript


    【解决方案1】:

    问题是minireducer 将是所有签名的联合,并且签名不可调用。

    打字稿3.3feature 来放宽此限制,但您的代码将无法正常工作,因为这将期望第一个参数是签名联合中所有参数的交集。

    一个解决方案,如果你不介意额外的函数调用是使用一个额外的函数,这将使编译器休息,对象将具有正确的签名:

    const reducer2 = (action: Actions, state: State) => {
        // r must have a property K that is a function taking Action<K> 
        function call<K extends Tags>(o: Action<K>, r: Record<K, (action: Action<K>, state: State) => State>, state: State) {
            r[o.type](o, state); // r[o.type] is  (action: Action<K>, state: State) => State which is callable with an Action<K> 
        }
        const newState = call(action, minireducers, state);
        return newState;
    }
    

    【讨论】:

    • 好的,检查时,这不适用于 strictFunctionTypes ,我不希望禁用任何严格标志。 (我更喜欢在本地转换为any)就像我写的那样,我对解决方案感兴趣,甚至纯粹从理论的角度来看,试图了解这样的东西是否可以用静态类型编写。这可能是不可能的。如果你想出一个不同的解决方案,如果你能分享它会很棒,我有点迷路了。如果没有人在一两天内回答将接受您的,则暂时删除接受。感谢您的帮助!
    • @AviadHadad 哎呀,我以为我打开了它......对不起
    猜你喜欢
    • 2021-03-20
    • 1970-01-01
    • 2023-03-24
    • 2021-12-21
    • 2017-09-22
    • 1970-01-01
    • 2018-10-06
    • 1970-01-01
    • 2019-02-08
    相关资源
    最近更新 更多