【问题标题】:React w/Typescript: useReducer, Action Interface w/ Union TypeReact w/Typescript: useReducer, Action Interface w/ Union Type
【发布时间】:2025-11-21 20:20:05
【问题描述】:

所以我尝试使用useReducer 挂钩创建一个reducer,该挂钩使用一个名为Action 的接口,该接口的属性可以是stringnumber

type Actions = 'update_foo' | 'update_bar';

interface Action {
    type: Actions;
    value?: number | string;
}

我还为初始状态定义了一个接口,并为设置默认状态定义了一个const

interface InitialState {
    foo: number;
    bar: string;
}

const defaultState: InitialState = {
    foo: 1,
    bar: 'bar'
}

然后是我的 reducer 函数:

const fooBarReducer: React.Reducer<InitialState, Action> = (state: InitialState, action: Action) => {
    switch(action.type) {
        case 'update_foo':
            return { ...state, foo: action.value };
        case 'update_bar':
            return { ...state, bar: action.value };
        default:
            return defaultState;
    }
}

我遇到的这个问题是 Typescript 似乎不喜欢联合类型 def 并抛出以下错误:

Type '(state: InitialState, action: Action) => { foo: string | number | undefined; bar: string | number; }' is not assignable to type 'Reducer<InitialState, Action>'. 
Call signature return types '{ foo: string | number | undefined; bar: string | number; }' and 'InitialState' are incompatible. The types of 'foo' are incompatible between these types. 
Type 'string | number | undefined' is not assignable to type 'string | number'. 
Type 'undefined' is not assignable to type 'string | number'.

我之前使用过联合类型,但没有使用 Reacts useReducer 钩子。这可以通过在Action 接口中为foobar 分别设置一个属性来解决,但如果可能的话,我想使用联合类型。

任何帮助将不胜感激!

【问题讨论】:

    标签: reactjs typescript


    【解决方案1】:

    问题似乎是您在字段级别使用类型联合。这意味着任何字段可以是任何类型(例如:type 可以是 update_foovalue 可以是 "str",这是无效的)。如果 Action interface 更新为动作的联合(而不是每个字段都是联合),则可以实现您正在寻找的类型安全。

    现在,当 typeupdate_foo 时,TypeScript 将知道 value 必须number。当typeupdate_bar 时,TypeScript 会知道value 必须string

    我还假设在update_bar 的情况下,应该是bar 而不是foo

    type Action =
      | { type: "update_foo"; value: number }
      | { type: "update_bar"; value: string };
    
    interface InitialState {
      foo: number;
      bar: string;
    }
    
    const defaultState: InitialState = {
      foo: 1,
      bar: "bar"
    };
    
    const fooBarReducer: React.Reducer<InitialState, Action> = (
      state: InitialState,
      action: Action
    ) => {
      switch (action.type) {
        case "update_foo":
          return { ...state, foo: action.value };
        case "update_bar":
          return { ...state, bar: action.value };
        default:
          return defaultState;
      }
    };
    

    有关更多详细信息,您可能正在寻找的模式是discriminated unions

    【讨论】:

    • 哈哈,是的,update_bar 的情况应该是 bar 而不是 foo。我已经更新了sn-p。但感谢这项工作,我很感激详尽的解释!