Использование redux-действий, redux-thunk и redux-обещания-промежуточного программного обеспечения в машинописи
Новое в машинописной + редуксной экосистеме здесь.
Как правильно инкапсулировать информацию о типе в асинхронные действия, когда в TypeScript используются избыточные-действия, избыточное-промежуточное и избыточное-обещание-промежуточное программное обеспечение?
Пример аутентификации:
/* actions */
const login = createAction(LOGIN, authService.login);
/* authService */
async function login(payload: LoginPayload): Promise<LoginResponse> {
// ... authenticate here.
}
Поскольку я использую промежуточное программное обеспечение redux-обещание, действия LOGIN_PENDING
, LOGIN_FULFILLED
/LOGIN_REJECTED
отправляются автоматически. Как мне создать типы для них, чтобы редуктор мог выяснить, с каким объектом действия он имеет дело?
Поскольку редукс-действия следует за FSA, _FULFILLED
должен иметь action.payload
, _REJECTED
должен иметь action.error
/* reducer */
function loginReducer(state: AppState, action: AuthAction) {
switch (action.type) {
case LOGIN_FULFILLED:
// action.payload should be defined as LoginResponse object here.
// action.error shouldnt be present.
case LOGIN_REJECTED:
// action.error should be defined
}
}
Как бы я пошел о создании AuthAction
тип? Я предполагаю, что это должен быть тип объединения каждого из отдельных типов действий (которые могут быть типами объединения самостоятельно). redux-actions
также обеспечивает Action
а также BaseAction
типы для этого.
1 ответ
"Нормальный" способ сделать это - указать все "интерфейсы действий" и тип объединения вплоть до редуктора. Затем включите тип. Но из примера кода не ясно, является ли тип AuthAction типом объединения...
например
type T = Object; //Your resolve data type
interface ILoginAction {type: "LOGIN", payload: {promise: Promise<T> }}
interface ILoginRejectedAction {type: "LOGIN_REJECTED", error: YourErrorType }
interface ILoginFulfilledAction {type: "LOGIN_FULFILLED", payload: {data: T }}
export type LoginActions = ILoginAction | ILoginRejectedAction | ILoginFulfilledAction
Разбавление:
import { LoginActions } from "./actions"; //Or where your actions are
function loginReducer(state: AppState, action: LoginActions) {
switch (action.type) {
case "LOGIN":
// action.payload should be defined as LoginResponse object here.
// action.error shouldnt be present.
case "LOGIN_REJECTED":
// action.error should be defined
}
}
Вероятно, вы можете создать этот союз более умным способом, используя некоторые обобщения, но это ручной подход.
Я бы рекомендовал использовать createAsyncAction
вместо того createAction
. Таким образом, вы можете определить типизацию следующим образом:
/* actions */
const login = createAsyncAction<
typeof LOGIN,
LoginResponse, // no need to wrap in Promise<>
null, // this is optional Meta,
[LoginPayload] // action creator arguments wrapped in array
>(LOGIN, authService.login);
Но боюсь, у меня нет ответа, когда login
это преобразователь:
/* actions */
const loginAction = createAsyncAction(LOGIN, authService.login)
const loginActionCreator = (data: LoginPayload) => async(dispatch: Dispatch) => {
await dispatch(loginAction(data));
}
// ERROR: () => void is not assignable to () => (dispatch) => void (pseudo code)