Как типизировать useReducer и есть ли в целом более аккуратные варианты?

Экспериментирую с useReducer, и столкнулся с такой проблемой. Например, есть массив каких-нибудь записей. Его надо получить, присвоить в стейт компонента, а потом вручную добавлять новые записи и удалять. Вопрос в том, как всё это дело правильно типизировать?

Я набросал такой вариант, но меня смущает несколько моментов:

Во-первых, явное присваивание трех возможных типов в payload:
type ActionType = {
  type: ActionPointsType;
  payload: AlbumType | AlbumType[] | number;
};


Во-вторых, явное указание типов в return функции reducer (все эти "as AlbumType[]"):
switch (action.type) {
    case ActionPoints.SET_ALBUMS:
      return action.payload as AlbumType[];
    case ActionPoints.ADD_ALBUM:
      return [...state, action.payload] as AlbumType[];
    case ActionPoints.REMOVE_ALBUM:
      return state.filter((album) => album.id !== action.payload);
    default:
      return state;
  }


В общем, как лучше типизировать функцию reducer и поля типа ActionType? Наверняка можно как-то применить здесь дженерики, но не могу сообразить, как именно. Пробовал, например, вот так:
type ActionType<T = any> = {
  type: ActionPointsType
  payload: T
};

// ...
const addNewAlbum = (event: React.FormEvent) => {
    event.preventDefault();

    const action: ActionType<AlbumType> = {
      type: ActionPoints.ADD_ALBUM,
      payload: {
        id: albums.length + 1,
        title: newAlbum
      }
    }

    dispatch(action);
  }


Ошибок никаких нет, но action.payload внутри функции reducer, а также albums в стейте моментально превращаются в any. Пробовал еще парочку вариантов, но везде какие-то свои косяки всплывают.

Второй вопрос, абстрагируясь от типизации - насколько мой вариант вообще, что называется, нормальный? Может, есть какие-либо более правильные или изящные способы реализовать подобную задачу?
  • Вопрос задан
  • 368 просмотров
Решения вопроса 1
bingo347
@bingo347 Куратор тега TypeScript
Crazy on performance...
type AlbumType = {
  id: number;
  title: string;
};

enum ActionPoints {
  SET_ALBUMS = "SET_ALBUMS",
  ADD_ALBUM = "ADD_ALBUM",
  REMOVE_ALBUM = "REMOVE_ALBUM"
}

type ActionType = {
  type: ActionPoints.SET_ALBUMS;
  payload: AlbumType[];
} | {
  type: ActionPoints.ADD_ALBUM;
  payload: AlbumType;
} | {
  type: ActionPoints.REMOVE_ALBUM;
  payload: number;
};

const reducer = (state: AlbumType[], action: ActionType) => {
  switch (action.type) {
    case ActionPoints.SET_ALBUMS:
      return action.payload;
    case ActionPoints.ADD_ALBUM:
      return [...state, action.payload];
    case ActionPoints.REMOVE_ALBUM:
      return state.filter((album) => album.id !== action.payload);
    default:
      return state;
  }
};
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы