Typescript 2.8条件类型和redux中间件

时间:2018-03-29 09:13:15

标签: reactjs typescript redux redux-promise typescript2.8

我使用typescript&#39> ReturnType 功能找到了关于type-safety redux的精彩解决方案,这是2.8版本的新功能。

动作/ types.ts

type FunctionType = (...args: any[]) => any;
type ActionCreatorsMapObject = { [actionCreator: string]: FunctionType };

export type ActionUnion<A extends ActionCreatorsMapObject> = ReturnType<A[keyof A]>;

模型/ user.ts

export interface User {
    id: number;
    username: string;
    name: string;
}

动作/ index.ts

import { User } from '../models/user';

interface Action<T extends string> {
    type: T;
}

interface ActionWithPayload<T extends string, P> extends Action<T> {
    payload: P;
}

export function createAction<T extends string>(type: T): Action<T>;
export function createAction<T extends string, P>(type: T, payload: P): ActionWithPayload<T, P>;
export function createAction<T extends string, P>(type: T, payload?: P) {
    return payload === undefined ? { type } : { type, payload };
}

export enum ActionTypes {
    SIMPLE_ACTION = 'SIMPLE_ACTION',
    ASYNC_ACTION = 'ASYNC_ACTION
}

export const Actions = {
    simpleAction: (value: string) => createAction(ActionTypes.SIMPLE_ACTION, value)
    asyncAction: () => {
        const token = localStorage.getItem('token');
        const request = axios.get('/', {
            headers: {
                'Authorization': token
            }
        });
        return createAction(ActionTypes.ASYNC_ACTION, request as AxiosPromise<User>);
    },
    anotherAction: (value: number) => blahblah...
};

export type Actions = ActionUnion<typeof Actions>;

在进入Reducer之前,我使用 redux-promise 软件包,它是redux的中间件来处理异步调度。简单地说,如果有效载荷是promise,那么 redux-promise 将解析该promise,并将有效载荷值更改为promise的结果。

这是问题所在。我想在编写reducer代码时使用 ActionUnion 类型。但是打字稿并不知道承诺已经被中间件解决了。非异步操作效果很好,因为中间件不会更改有效负载。

user_reducer.ts

import { User } from '../models/user';
import * as fromActions from '../actions';

interface UserState {
    loginUser: User | null;
    someValue: string;
}

const initialState: UserState = {
    loginUser: null,
    someValue: ''
};

export default (state: UserState = initialState, action: fromActions.Actions) => {
    switch (action.type) {
        case fromActions.ActionTypes.SIMPLE_ACTION: {
            // typescript knows that action.payload is string. It works well.
            const data = action.payload;
            ...
        }
        case fromActions.ActionTypes.ASYNC_ACTION: {
            // typescript knows that action.payload is still promise.
            // Therefore typescript said, action.payload.data is wrong.
            const username = action.payload.data.username;
            ...
        }
        ...
    }
};

这不是错误。这是显而易见的,因为我定义了 action:fromActions.Action ,因此打字稿认为&#34;哦,action类型参数是asyncAction函数的ReturnValue,它有promise对象作为有效负载值。&#34 ;

我认为有两种解决方案。

1。旧式

在2.8之前,我们通常定义每个ActionCreator的界面并将它们联合起来。

export type Actions = ActionCreator1 | ActionCreator2 | ActionCreator3 | ...;

它可以解决中间件问题,但是如果我改变动作创建函数的返回值,那么我必须手动更改匹配的接口。 (这就是为什么新的 ReturnValue 功能很棒的原因。)

2。重新定义操作类型

而不是使用,

export type Actions = ActionUnion<typeof Actions>;

让我们定义具有承诺作为有效载荷值的Action类型。让我们说一下ActionWithPromisePayload。 下面是伪代码。我不擅长打字稿。 (T_T)

// Let's define two new types.
type ActionWithPromisePayload<...>;
type ActionWithPromiseResolvedPayload<...>;

ActionWithPromisePayload 用于检查操作对象的有效负载是否具有承诺。 ActionWithPromiseResolvedPayload 用于重新定义动作类型,因为承诺由中间件解决。

然后,使用条件类型重新定义类型 Actions ,这也是2.8中的新功能。

如果Action将promise对象作为有效负载,那么它的实际类型不是ActionValue,而是已解决的类型。 下面是伪代码。

export type Actions = 
    ActionUnion<typeof Actions> extends ActionWithPromisePayload<..> ? ActionWithPromiseResolvedPayload<..> : ActionUnion<typeof Actions>;

问题有点混乱,但关键问题是,如何很好地定义reducer的动作参数类型并与中间件配合良好?

如果有更好的方法,那就不要关心我的解决方案。 感谢。

参考。 https://medium.com/@martin_hotell/improved-redux-type-safety-with-typescript-2-8-2c11a8062575

0 个答案:

没有答案