#typescript #generics #redux #typescript-generics
#typescript #дженерики #redux #typescript-дженерики
Вопрос:
Привет
Я пытаюсь уменьшить Bolilderplate в своем приложении react. Я использую состояние Redux и решил, какую структуру Redux использовать (см. Контекст). Теперь я хочу сделать его меньше. Для этого я создал универсальный ActionCreator . Моя проблема состоит в том, чтобы найти желаемый общий синтаксис.
Создатель универсального действия
Это мой общий подход к генератору действий.
export class genericAction<M, K> extends Action {
public reducer = (state: M) => ({ ...state, ...this.payload });
constructor(public readonly type: string | undefined, public payload: K) {
super();
}
}
На данный момент возможно только объединение состояний, но я стремлюсь к необязательному параметру, который является функцией. Затем эта функция позволит мне выполнять пользовательские слияния состояний (например, state.count полезная нагрузка). Но это другая тема.
Проблема
Моя проблема в том, что я мог бы использовать { fubar: number}
вместо { loading: boolean }
. Очевидно, что fubar не является частью моей ProductStateModel.
const someAction = new genericAction<ProductStateModel, { loading: boolean }>(ProductListActionTypes.REQUEST_START, { loading: true });
Вопрос
Я хочу использовать только атрибуты ProductStateModel в качестве типа для общего K. Псевдокод:
genericAction<M, K is type of M> extends Action
Общая идея состоит в том, чтобы создать действие со следующими типами и параметрами:
- StateModel
- ProductListActionType
- Полезная нагрузка
Возможно ли это? Или есть общее / альтернативное решение? Я согласен с изменениями в StateModel, если это необходимо. Я новичок в redux и хочу сделать это правильно. Я попытался получить Pick<T, K> в общем методе, но к настоящему времени я не уверен, возможно ли это вообще так, как мне нужно, или, может быть, я должен просто пойти спать: D
Спасибо за любые подсказки и помощь
Контекст
Это моя отправная точка
// State
export interface State {}
export interface ProductStateModel extends State {
products: Array<ProductDTO>;
loading: boolean;
error: string;
}
// Product Actions
export abstract class Action {
public readonly type: string | undefined;
protected constructor() {
return Object.assign({}, this);
}
abstract reducer(state: State): State;
}
// First of 3 Actions. They are all very similar. The generic function should replace all of them.
export class ProductListRequest extends Action {
public readonly type = ProductListActionTypes.REQUEST_START;
public reducer = (state: ProductStateModel) => ({ ...state, loading: this.payload.loading });
constructor(public payload: Pick<ProductStateModel, 'loading'>) {
super();
}
}
// Reducer
export const productListReducer = (state: ProductStateModel = defaultProductState, action: Action) => {
return isNil(action.reducer) ? action.reducer(state) : state;
};
Ответ №1:
export class genericAction<M> extends Action {
public reducer = (state: M) => ({ ...state, ...this.payload });
constructor(public readonly type: string | undefined, public payload: Partial<M>) {
super();
}
}
Я удалил второй тип и добавил частичный к общему типу полезной нагрузки. В моей голове я хотел ограничить разрешенные атрибуты, как я делал в оригинальном подходе, Pick<ProductStateModel, 'loading'>
но потом я понял, что это не нужно. Новый вызов универсального класса — это единственное место, где я определяю, какие атрибуты необходимы для этого действия, и это нормально.