Как создать определение типа для действия ловушки React useReducer в Typescript? - PullRequest
2 голосов
/ 03 марта 2020

Допустим, у нас userReducer определено так:

function userReducer(state: string, action: UserAction): string {
  switch (action.type) {
    case "LOGIN":
      return action.username;
    case "LOGOUT":
      return "";
    default:
      throw new Error("Unknown 'user' action");
  }
}

Каков наилучший способ определения типа UserAction, поэтому можно будет вызывать dispatch как с username полезной нагрузкой, так и с без:

dispatch({ type: "LOGIN", username: "Joe"}});
/* ... */
dispatch({ type: "LOGOUT" });

Если тип определен следующим образом:

type UserActionWithPayload = {
  type: string;
  username: string;
};

type UserActionWithoutPayload = {
  type: string;
};

export type UserAction = UserActionWithPayload | UserActionWithoutPayload;

Выдает компилятор и ошибка в редукторе в случае «ВХОДА»: TS2339: Property 'username' does not exist on type 'UserAction'. Property 'username' does not exist on type 'UserActionWithoutPayload'.

Если тип определяется с необязательным членом:

export type UserAction = {
  type: string;
  username?: string;
}

Тогда компилятор показывает эту ошибку: TS2322: Type 'string | undefined' is not assignable to type 'string'. Type 'undefined' is not assignable to type 'string'.

Чего здесь не хватает? Может быть, весь подход неверен?

Проект использует TypeScript 3.8.3 и React. js 16.13.0.

Ответы [ 4 ]

3 голосов
/ 04 марта 2020

Необходимо указать указанному c случаю редуктора дополнительную информацию о типе путем приведения типа в действие

type UserActionWithPayload = {
  type: string;
  username: string;
};

type UserActionWithoutPayload = {
  type: string;
};

type UserAction = UserActionWithPayload | UserActionWithoutPayload;

function userReducer(state: string, action: UserAction): string {
  switch (action.type) {
    case "LOGIN":
      return (action as UserActionWithPayload).username;
    case "LOGOUT":
      return "";
    default:
      throw new Error("Unknown 'user' action");
  }
}

let state = "";
state = userReducer(state, { type: "LOGIN", username: "John" });
console.log("LOGIN", state);

state = userReducer(state, { type: "LOGOUT" });
console.log("LOGOUT", state);

Playground Link

1 голос
/ 03 марта 2020

Подход выглядит нормально, проблема в том, что ваш редуктор имеет тип возврата string, но если ему передано UserActionWithoutPayload, то он может вернуть action.username, где имя пользователя не определено.

Так Один из способов исправить это - ослабить тип возврата:

function userReducer(state: string, action: UserAction): string | undefined {
  switch (action.type) {
    case "LOGIN":
      return action.username;
    case "LOGOUT":
      return "";
    default:
      throw new Error("Unknown 'user' action");
  }
}
0 голосов
/ 04 марта 2020

После нескольких часов копания и экспериментирования было найдено довольно элегантное решение с помощью Typescript enum и типов объединения для действий:

enum UserActionType {
  LOGIN = "LOGIN",
  LOGOUT = "LOGOUT"
}

type UserState = string;

type UserAction =
| { type: UserActionType.LOGIN; username: string }
| { type: UserActionType.LOGOUT }

function userReducer(state: UserState, action: UserAction): string {
  switch (action.type) {
    case UserActionType.LOGIN:
      return action.username;
    case UserActionType.LOGOUT:
      return "";
    default:
      throw new Error();
  }
}

function App() {
  const [user, userDispatch] = useReducer(userReducer, "");

  function handleLogin() {
    userDispatch({ type: UserActionType.LOGIN, username: "Joe" });
  }

  function handleLogout() {
    userDispatch({ type: UserActionType.LOGOUT });
  }

  /* ... */
}

Нет ошибок или предупреждений с использованием подхода, описанного выше, плюс существует довольно строгий контракт на использование действия.

0 голосов
/ 03 марта 2020
function userReducer(state: string, action: UserAction): any {
  switch (action.type) {
    case "LOGIN":
      return action.username;
    case "LOGOUT":
      return "";
    default:
      throw new Error("Unknown 'user' action");
  }
}

В этой функции вы определили тип возвращаемого значения как строку. Но когда вы передаете UserAction как тип UserActionWithoutPayload, возвращаемое значение должно быть неопределенным. Эти причины различаются между типом возврата функции userReducer (...) и реальным типом возврата в виде строки и не определены. Чтобы исправить это, вы можете изменить тип возврата как любой.

Я надеюсь, что этот ответ может быть полезным в вашей работе.

С уважением. Лин.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...