Главная Категории Контакты Поиск

Условия типов в Redux

Что такое условия типов или type guards и как их использовать в Redux.

TypeScript ·12.11.2019·читать 3 мин 🤓·Автор: Alex Myzgin

В данной статье мы рассмотрим как создать условия типов или type guards на примере redux reducers, эти принципы также могут применяться к любой функции.

У нас есть todoReducer, который принимает state. Он имеет тип Todo[] и значение по умолчанию, а также action с типом Action.

import { Todo, Action, ActionTypes } from "../actions";

export const todosReducers = (state: Todo[] = [], action: Action ) => {
  switch (action.type) {
    case ActionTypes.fetchTodos:
      return action.payload;
    case ActionTypes.deleteTodo:

    default:
      return state;
  }
}
export interface Todo {
  id: number;
  title: string;
  completed: boolean;
}

export interface FetchTodosAction {
  type: ActionTypes.fetchTodos;
  payload: Todo[];
}

export interface DeleteTodoAction {
  type: ActionTypes.deleteTodo;
  payload: number;
}

export enum ActionTypes {
  fetchTodos,
  deleteTodo
}

export type Action = FetchTodosAction | DeleteTodoAction;

Action это type; который является объединением интерфейсов FetchTodosAction и DeleteTodoAction.

Каждое action в redux должно иметь как минимум type. В нашем случае мы предоставили type в виде enum ActionTypes, который имеет fetchTodos и deleteTodo.

Во всех этих различных случаях (cases), оператор switch действует как условия типов (type guards). Условия типов (type guards) уменьшают количество различных случаев (cases) внутри нашего объединения типов (type Action).

Поэтому, если мы наведём курсором мыши на action внутри case ActionTypes.deleteTodo:, то из-за этого оператора, typescript с абсолютной уверенностью знает, что action будет иметь тип DeleteTodoAction. И причина этого в том, что мы доберемся до action только в том случае, если свойство action.type будет равно deleteTodo.

import { Todo, Action, ActionTypes } from "../actions";

export const todosReducers = (state: Todo[] = [], action: Action ) => {
  switch (action.type) {
    case ActionTypes.fetchTodos:
      return action.payload;
    case ActionTypes.deleteTodo:
      action; // action: DeleteTodoAction
    default:
      return state;
  }
}

Вот настоящая магия redux и typescript вместе взятых.

Всё сводится к настройке псевдонима типа действия type Action и перечисления типов действий enum ActionTypes путем настройки этих двух вещей вместе с оператором switch.

export interface FetchTodosAction {
  type: ActionTypes.fetchTodos;
  payload: Todo[];
}

export interface DeleteTodoAction {
  type: ActionTypes.deleteTodo;
  payload: number;
}

export enum ActionTypes {
  fetchTodos,
  deleteTodo
}

export type Action = FetchTodosAction | DeleteTodoAction;

В каждом из разных случаев мы точно знаем, с каким типом объекта мы работаем внутри оператора case. Мы знаем с абсолютной уверенностью, что свойство action внутри ActionTypes.deleteTodo будет равно DeleteTodoAction.

export const todosReducers = (state: Todo[] = [], action: Action ) => {
  switch (action.type) {
    case ActionTypes.fetchTodos:
      return action.payload;
    case ActionTypes.deleteTodo:
      action; // action: DeleteTodoAction
    default:
      return state;
  }
}

И абсолютно точно знаем то, что payload будет числом, представляющим id, для того поля, которое хотим удалить.

export const todosReducers = (state: Todo[] = [], action: Action ) => {
  switch (action.type) {
    case ActionTypes.fetchTodos:
      return action.payload;
    case ActionTypes.deleteTodo:
      action.payload; // DeleteTodoAction.payload: number
    default:
      return state;
  }
}

Вот так мы действительно получаем отличную функциональность или сотрудничество между TypeScript и Redux.

Давай добавим логику для удаления задачи. Для этого мы вернем state, отфильтруем все задачи и оставим только те, где идентификатор задачи не равен action.payload. Именно так мы удалим задачу, которая соответствует идентификатору.

export const todosReducers = (state: Todo[] = [], action: Action ) => {
  switch (action.type) {
    case ActionTypes.fetchTodos:
      return action.payload;
    case ActionTypes.deleteTodo:
      return state.filter((todo: Todo) => todo.id !== action.payload);
    default:
      return state;
  }
}

Пример кода можно посмотреть здесь.

Website, name & logo
Copyright © 2019. Alex Myzgin