【问题标题】:Switch inside a function doesn't work correctly in Typescript函数内部的开关在 Typescript 中无法正常工作
【发布时间】:2021-03-17 05:47:56
【问题描述】:

我正在尝试将 Typescript 与 Redux 一起使用,并根据 Redux Ducks 进行设计。我在这里与这种类型检查作斗争,我不知道为什么。


const UPDATE = "ticTacToe/board/update";
const RESET = "ticTacToe/board/reset";

export default function reducer(
  state = initialState,
  action: IBoardActions
): IBoardState {
  switch (action.type) {
    case UPDATE:
      const { nextPlayer, clickedIndex } = action;
            // TypeScript error: Property 'nextPlayer' does not exist on type 
           //'{ type: string; nextPlayer: string; clickedIndex: number; } | { type: string; }'.  TS2339
      const newSquares = [...state.squares];
      newSquares[clickedIndex] = nextPlayer;
      return {
        squares: newSquares,
        oIsNext: !state.oIsNext
      };
    case RESET:
      return initialState
    default:
      return state;
  }
}

type BoardAction = typeof updateBoard | typeof resetBoard;

export type IBoardActions = ReturnType<BoardAction>;

export function updateBoard(nextPlayer: string, clickedIndex: number) {
  return {
    type: UPDATE,
    nextPlayer,
    clickedIndex
  };
}

export function resetBoard() {
  return {
    type: RESET
  };
}

我尝试使用enum 修复它,但这也不起作用。是switch的问题吗?

enum Action {
  UPDATE = 'update',
  RESET = 'reset'
}

export default function reducer(
  state = initialState,
  action: IBoardActions
): IBoardState {
  switch (action.type) {
    case Action.UPDATE:
      const { nextPlayer, clickedIndex } = action;
      //TypeScript error: Property 'nextPlayer' does not exist on type '{ type: Ac
tion; nextPlayer: TicTacToeSymbols; clickedIndex: number; } | { type: Acti
on; }'.  TS2339
      const newSquares = [...state.squares];
      newSquares[clickedIndex] = nextPlayer;
      return {
        squares: newSquares,
        oIsNext: !state.oIsNext
      };
    case Action.RESET:
      return initialState
    default:
      return state;
  }
}

type BoardAction = typeof updateBoard | typeof resetBoard;

export type IBoardActions = ReturnType<BoardAction>;

export function updateBoard(nextPlayer: TicTacToeSymbols, clickedIndex: number) {
  return {
    type: Action.UPDATE,
    nextPlayer,
    clickedIndex
  };
}

export function resetBoard() {
  return {
    type: Action.RESET
  };
}

为什么会出现此错误?根据错误消息,我的输入似乎是正确的。

【问题讨论】:

  • 看来您需要使用discriminated union。请注意,可区分联合需要文字字符串类型,而不是一般的 string 类型。现在,IBoardActions 的类型为 { type: string; nextPlayer: string; clickedIndex: number; } | { type: string; }。注意type 属性的类型是string不是字符串文字。
  • 根据类型,操作可以是updateBoardresetBoard(参见| 运算符)。 resetBoard 动作中不存在下一个玩家。因此,TS 也在抱怨同样的事情。添加可选链接?. 或在resetAction 中添加nextPlayer
  • @Vishnu 可选链接在这种情况下不起作用。可选链接用于访问可为空的属性,而不是可能不存在的属性。必须先缩小联合类型。
  • OP,请尝试在打字稿操场上制作一个最小的可重现示例。包括UPDATERESET 的定义。具体来说,它们的类型必须是常量字符串文字才能使可区分的联合起作用。
  • @Chase 是的,我的错。 if 条件应该没问题。

标签: reactjs typescript redux


【解决方案1】:

您正在寻找discriminated unions。请注意,您的 sn-p 中 IBoardActions 的类型是-

{ type: string; nextPlayer: string; clickedIndex: number; } | { type: string; }

但实际上,你想要-

{ type: typeof UPDATE; nextPlayer: string; clickedIndex: number; } | { type: typeof RESET; }

这明确表明,仅当type 属性与UPDATE 具有相同类型时才使用第一种替代方法,RESET 也是如此。

但在您的情况下,UPDATERESET 都是 string 类型。没有办法歧视他们。

这是字符串字面量类型的用武之地,将UPDATERESET 的定义更改为-

const UPDATE = "ticTacToe/board/update" as const;
const RESET = "ticTacToe/board/reset" as const;

as const 语法称为const assertions。如果您注意到,UPDATE 的类型现在已成为设置为其值的字符串文字,RESET 也是如此。

这是playground demo

【讨论】:

  • 谢谢@Chase 我认为这是我需要的解决方案。但我不明白为什么enum 也不适用于此,您对此有什么想法吗?
  • @JohnWinston 枚举键不是区分联合的正确用例。所有枚举键都具有相同的类型 - 枚举本身的类型。他们之间没有歧视。
【解决方案2】:

你也可以使用这样的界面。

export interface IBoardActions extends ReturnType<typeof updateBoard>, ReturnType<typeof resetBoard>

【讨论】:

    【解决方案3】:

    我们不再像they have their own set of problems 那样鼓励使用有区别的联合,并且需要编写大量额外的代码。

    官方建议使用官方的 redux 工具包,该工具包考虑到了 TypeScript 支持,这将大大减少您的代码。请查看official redux tutorial on modern redux 了解简短介绍,然后查看essentials tutorial 了解更深入的信息。

    【讨论】:

      猜你喜欢
      • 2016-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-08
      • 1970-01-01
      • 2012-03-31
      相关资源
      最近更新 更多