JavaRush /Курсы /Модуль 3: React /Типизация генераторов и саг с TypeScript

Типизация генераторов и саг с TypeScript

Модуль 3: React
7 уровень , 6 лекция
Открыта

Проблема

Redux Saga базируется на использовании генераторов (generator functions) для упрощённого управления побочными эффектами, таких как вызовы API, задержки и подписки на действия. Главный инструмент в Redux Saga — это эффекты, такие как call, put, takeLatest и другие.

Однако генераторы по своей природе сложны для типизации. В отличие от стандартных функций, генераторы работают итеративно, возвращая значения через yield. Это создаёт уникальный набор вызовов для типизации в TypeScript.

Типизация генераторов в TypeScript

Генераторы в JavaScript представляют собой функции, которые "замораживают" выполнение через yield. Вот простой пример генератора:

function* exampleGenerator() {
  yield 'Hello';
  return 'World';
}

const generator = exampleGenerator();
console.log(generator.next()); // { value: 'Hello', done: false }
console.log(generator.next()); // { value: 'World', done: true }

Теперь добавим типизацию:

function* exampleGenerator(): Generator<string, string, void> {
  yield 'Hello';
  return 'World';
}

Разбор Generator<T, TReturn, TNext>

  • T: тип значения, возвращаемого через yield.
  • TReturn: тип значения, возвращаемого с помощью команды return.
  • TNext: тип значения, ожидаемого через next().

Пример с переданным значением

function* numberGenerator(): Generator<number, string, number> {
  const input = yield 42; // Ожидаем число через next()
  return `Received: ${input}`;
}

const gen = numberGenerator();
console.log(gen.next()); // { value: 42, done: false }
console.log(gen.next(7)); // { value: 'Received: 7', done: true }

Типизация саг в Redux Saga

Саги в Redux Saga — это генераторы, которые взаимодействуют с эффектами, такими как call, put, takeEvery и др. Здесь типизация становится ещё более важной, так как мы часто работаем с параметрами, действиями и асинхронными вызовами.

Типизация эффекта call

call используется для вызова функций. Его типизация зависит от сигнатуры вызываемой функции:

import { call } from 'redux-saga/effects';

// Функция, которую будем вызывать
async function fetchData(id: number): Promise<string> {
  return `Data for ID: ${id}`;
}

// Типизированный генератор с использованием call
function* fetchSaga() {
  const data: string = yield call(fetchData, 5);
  console.log(data); // Data for ID: 5
}

Типизация эффекта put

put используется для отправки действий. Здесь важно указать тип действия (action):

import { put } from 'redux-saga/effects';

interface MyAction {
  type: string;
  payload: number;
}

// Сага отправляет типизированное действие
function* sendActionSaga() {
  const action: MyAction = { type: 'MY_ACTION', payload: 42 };
  yield put(action);
}

Типизация takeEvery

takeEvery подписывается на типы действий и вызывает сага-функцию при их возникновении. Важно учитывать тип действия:

import { takeEvery, put } from 'redux-saga/effects';

interface MyAction {
  type: 'MY_ACTION';
  payload: number;
}

// Обработчик действия
function* handleAction(action: MyAction) {
  console.log(`Payload received: ${action.payload}`);
  yield put({ type: 'ACTION_HANDLED' });
}

// Типизированная подписка
function* watchActions() {
  yield takeEvery<MyAction>('MY_ACTION', handleAction);
}

Практическое применение типизации Saga

Давайте расширим наше приложение и добавим асинхронные запросы через Redux Saga. Мы создадим приложение, которое будет получать данные с API и обрабатывать их.

Создаём срез slice состояния

import { createSlice, PayloadAction } from '@reduxjs/toolkit';

interface DataState {
  data: string[];
  loading: boolean;
}

const initialState: DataState = {
  data: [],
  loading: false,
};

const dataSlice = createSlice({
  name: 'data',
  initialState,
  reducers: {
    fetchDataRequest(state) {
      state.loading = true;
    },
    fetchDataSuccess(state, action: PayloadAction<string[]>) {
      state.loading = false;
      state.data = action.payload;
    },
    fetchDataFailure(state) {
      state.loading = false;
    },
  },
});

export const { fetchDataRequest, fetchDataSuccess, fetchDataFailure } = dataSlice.actions;
export default dataSlice.reducer;

Сага для обработки запросов

import { call, put, takeEvery } from 'redux-saga/effects';
import { fetchDataRequest, fetchDataSuccess, fetchDataFailure } from './dataSlice';

async function fetchApi(): Promise<string[]> {
  return ['React', 'Redux', 'TypeScript'];
}

function* fetchDataSaga() {
  try {
    const data: string[] = yield call(fetchApi); // Типизация call
    yield put(fetchDataSuccess(data)); // Типизация put
  } catch (error) {
    yield put(fetchDataFailure());
  }
}

export function* watchFetchData() {
  yield takeEvery(fetchDataRequest.type, fetchDataSaga);
}

Интеграция Saga в Store

import createSagaMiddleware from 'redux-saga';
import { configureStore } from '@reduxjs/toolkit';
import dataReducer from './dataSlice';
import { watchFetchData } from './sagas';

const sagaMiddleware = createSagaMiddleware();

const store = configureStore({
  reducer: {
    data: dataReducer,
  },
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(sagaMiddleware),
});

sagaMiddleware.run(watchFetchData);

export default store;

Полезные советы и особенности

Работая с TypeScript и Redux Saga, вы можете столкнуться с несколькими особенностями:

  1. call и put возвращают значения через yield. Помните об этом, чтобы правильно указывать типы.
  2. Ошибки в саге часто сложно отлаживать без настройки try/catch. Добавляйте типизацию для обработки ошибок.
  3. Используйте интерфейсы для типизации действий. Это особенно важно для методов вроде takeEvery или takeLatest, так как они обрабатывают действия.

Типизация генераторов и саг делает код более безопасным и понятным. Теперь вы сможете эффективно управлять сложными асинхронными процессами и минимизировать ошибки в Redux-приложениях!

1
Задача
Модуль 3: React, 7 уровень, 6 лекция
Недоступна
Типизация генераторов
Типизация генераторов
1
Задача
Модуль 3: React, 7 уровень, 6 лекция
Недоступна
Типизация call эффекта
Типизация call эффекта
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ