Как использовать общие типы с обязательными полями в useReducer?

Я пытаюсь создать собственный хук React, используя useReducer. Предполагается, что он хранит состояние для массива объектов, и все объекты должны иметь поле «id». Структура объекта, отличная от идентификатора, должна быть общей.

Вот что у меня есть;

export enum ArrayActions { INIT, ADD, UPDATE, DELETE }

type ArrayAction<T> =
  | { type: ArrayActions.INIT, payload: T[] }
  | { type: ArrayActions.ADD, payload: T }
  | { type: ArrayActions.UPDATE, payload: T }
  | { type: ArrayActions.DELETE, id: string }

type ObjWithId = { id:any, [key: string]: any }

function arrayReducer<T extends ObjWithId>(state: T[], action: ArrayAction<T>): T[] {
  switch(action.type){
    case ArrayActions.INIT:
      return [...action.payload]
    case ArrayActions.ADD: // TODO: check if id already exists
      return [...state, action.payload]
    case ArrayActions.UPDATE:
      return [...(state.filter(item => item.id !== action.payload.id)), action.payload]
    case ArrayActions.DELETE:
      return state.filter(item => item.id !== action.id)
  }
}

export function useArrayReducer<T extends ObjWithId> (initialState: T[] = []): [T[], React.Dispatch<ArrayAction<T>>] {
  const [state, dispatch] = useReducer(arrayReducer, initialState)
  return [state, dispatch]      // error!
}

state в последней строке выдает следующую ошибку;

Type 'ObjWithId[]' is not assignable to type 'T[]'.
Type 'ObjWithId' is not assignable to type 'T'.

Я думал, что это может быть связано с определением ObjWithId, но не мог точно понять это. Не могли бы вы помочь мне достичь желаемого результата?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
0
202
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В случае, если это кому-то нужно;

export interface ListItem { 
  id: string | number | undefined;
  [key: string]: any;
}

export type Action<L extends ListItem> = 
  | { type: "init", items: L[]}
  | { type: "add", item: L }
  | { type: "update", item: L }
  | { type: "delete", id: NonNullable<L["id"]>}

Другие вопросы по теме