Как напечатать подсказку с редуксным диспетчером с типом возврата, отличным от типа отправки? - PullRequest
4 голосов
/ 20 мая 2019

Я хочу напечатать подсказку создателю действия другого типа, чем тип отправки.

Я пытался напечатать ThunkResult с обоими типами, но это не идеально.

// types.ts
interface AppListAction extends FetchAction {
  type: typeof APP_LIST,
  [FETCH]: {},
}

interface AppListSuccessAction extends FetchResponseAction {
  type: typeof APP_LIST_SUCCESS,
  response: Array<ListModel>
}

export type AppResponseActions =
  AppListSuccessAction

export type AppActions =
  AppListAction
  | AppResponseActions


// actions.ts
export const loadListCreator = (): AppActions => ({
  type: C_LIST,
  [FETCH]: {},
})

export const loadList = (): ThunkResult<AppResponseActions> => (dispatch: ThunkDispatcher) => dispatch(loadListCreator())

Я не ожидаю ошибок, но вместо этого получаю:

Ошибка TypeScript: тип «AppActions» не может быть назначен типу «AppResponseActions». Тип «AppListAction» нельзя назначить типу «AppResponseActions». Свойство response отсутствует в типе AppListAction, но требуется в типе AppListSuccessAction. TS2322

1 Ответ

0 голосов
/ 27 мая 2019

Чтобы сделать ваш код компилируемым, я сделал следующие предположения

import { ThunkAction, ThunkDispatch } from 'redux-thunk';

const FETCH = 'fetch';

interface FetchAction {
    [FETCH]: object;
}

interface FetchResponseAction {
    prop2: string;
}

const APP_LIST = 'APP_LIST';
const APP_LIST_SUCCESS = 'APP_LIST_SUCCESS';

interface ListModel {
    prop3: string;
}

А после вашего кода

type ThunkResult<R> = ThunkAction<R, State, undefined, AppActions>;
type ThunkDispatcher = ThunkDispatch<State, undefined, AppActions>;

С учетом приведенных выше предположений ваш код становится компилируемым со следующей модификацией

export const loadList = (): ThunkResult<AppActions> => (dispatch: ThunkDispatcher) => dispatch(loadListCreator());

ThunkResult<AppActions> должен иметь тип для R = AppActions. R - это возвращаемое значение из функции типа ThunkResult. В Redux-Thunk исходный код ThunkAction определяется следующим образом

export type ThunkAction<
    TReturnType,
    TState,
    TExtraThunkArg,
    TBasicAction extends Action
> = (
    dispatch: ThunkDispatch<TState, TExtraThunkArg, TBasicAction>,
    getState: () => TState,
    extraArgument: TExtraThunkArg
) => TReturnType;

То есть ThunkAction (на котором основан ThunkResult) - это функция, которая возвращает значение типа TReturnType.

Затем вы пытаетесь сделать loadlist типа ThunkResult<> равным (dispatch: ThunkDispatcher) => dispatch(loadListCreator()). По сути это также функция, но возвращающая результат вызова dispatch. dispatch имеет тип ThunkDispatcher и из источника , имеет тип

export interface ThunkDispatch<
    TState,
    TExtraThunkArg,
    TBasicAction extends Action
> {
    <TReturnType>(
        thunkAction: ThunkAction<TReturnType, TState, TExtraThunkArg, TBasicAction>
    ): TReturnType;
    <A extends TBasicAction>(action: A): A;
}

Таким образом, перегруженная функция принимает функцию с одним пакетом типа A и возвращает результат типа A. Поскольку dispatch вызывается с результатом вызова loadListCreator(), который возвращает результат типа AppActions, A выводится как AppActions.

Исходный код правильно не скомпилирован, так как вы хотите назначить функцию, возвращающую AppActions, функции, возвращающей AppResponseActions. AppActions является типом объединения и может быть либо AppResponseActions, либо AppListAction. Если это AppListAction, его нельзя присвоить AppResponseActions.

Кажется, я четко описал: -)

С практической точки зрения ваш код немного усложнен.

Подумайте об упрощении этого пути.

  1. Унаследовать все действия от Тип действия

    import { Action } from 'redux';
    
    interface AppListAction extends Action {
        type: typeof APP_LIST,
        [FETCH]: {},
    }
    
  2. Установить тип ThunkResult на следующий

    type ThunkResult = ThunkAction<void, State, undefined, AppActions>;
    

    Скорее всего, вам не нужно TReturnType. Thunk-действия просто отправляют все необходимое внутри кода и ничего не возвращают (void).

  3. Создатели действий для синхронизации действий могут выглядеть как

    interface SyncAction1 extends Action {
        type: 'SYNC_ACTION',
        argument: string
    }
    
    const SomeSyncAction = (argument: string) => <SyncAction1>{ type: 'SYNC_ACTION', argument }
    
  4. Создатели действий для асинхронных действий могут выглядеть как

    export const loadList = (): ThunkResult => (dispatch: ThunkDispatcher) => {
         dispatch(/* start loading */);
         const result = await // load api call
         dispatch(loadListCreator(result));
    }
    
  5. Редуктор будет выглядеть

    export function loadReducer (state = initalState, action: AppActions)
    // reducer code
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...