如何编写没有参数的类型化操作



Github的一个问题建议我们可以使用TypedAction.defineWithoutPayload来实现这一目的,但我找不到任何相关的例子来说明如何做到这一点。

当accessToken存储在有效负载中时,我使用它进行登录。如果存在令牌,则用户可以访问私有页面。

export const login = (token: string) => typedAction("LOGIN", token);

现在,在注销按钮上,我试图实现一个操作,删除有效负载中存储的值。在这种情况下,将没有用于调度操作的参数。那么我该怎么写打字动作呢?

如果我使用:

export const logout = () => typedAction("LOGOUT");

我开始在reducer的有效负载上得到一个错误,该属性在类型logout中不存在。

这是我的减速器:

export const tokenReducer = (
state: IState['token'] = null,
{ type, payload }: AppAction,
): typeof state => {
switch (type) {
case 'LOGIN':
return payload;
case 'LOGOUT':
return null;
default:
return state;
}
};

Codesandbox:https://codesandbox.io/s/keen-brook-kntkm?file=/src/store/actions/login.ts:50-118

电子邮件:c@c.com密码:检查

编辑:

export interface IState {
token: string | null;
}
const initialState: IState = {
token: null
};

如果我按照IDE的建议使用state: typeof initialStatestate = initialState,则action.payload上出现错误:

Type 'string' is not assignable to type 'IState'.ts(2322)

如果我尝试state: initialState,那么很明显:

'initialState' refers to a value, but is being used as a type here. Did you mean 'typeof initialState'?ts(2749)
``

定义typedAction函数的方式很好:

export function typedAction<T extends string>(type: T): { type: T };
export function typedAction<T extends string, P extends any>(
type: T,
payload: P
): { type: T; payload: P };
export function typedAction(type: string, payload?: any) {
return { type, payload };
}

你遇到的问题是因为你的减速器参数中的动作被破坏了:

export const tokenReducer = (
state: IState["token"] = null,
{ type, payload }: AppAction
): typeof state => {
// ...
};

析构函数和TypeScript的困难之一是,一旦这样做,变量的类型就会变得相互独立。将动作分解为{ payload, type }会产生type: 'LOGIN' | 'LOGOUT'payload: string | undefined变量。即使稍后细化type的值,就像在switch语句中一样,payload仍然具有类型string | undefined;在细化type之后,TypeScript不会自动细化事例块中payload的类型;他们的打字完全独立。

因此,你可以使用的一个有点丑陋的破解方法是不破坏:

export const tokenReducer = (
state: IState['token'] = null,
action: AppAction,
): typeof state => {
switch (action.type) {
case 'LOGIN':
return action.payload;
case 'LOGOUT':
return null;
default:
return state;
}
};

这之所以有效,是因为在switch语句中,它能够将action: AppAction类型细化为更具体的登录或注销类型,因此action.payload现在与其中一个操作特定的有效负载类型紧密相连。

这里有一种我使用的redux操作的替代模式,您可能会在我的fork中发现它更方便,它可以让您享受映射类型的功能,以更少的样板定义reducer。首先,您必须用类型/有效负载映射定义一个类型,并定义从中派生的一些类型:

export type ActionPayloads = {
LOGIN: string;
LOGOUT: void;
};
export type ActionType = keyof ActionPayloads;
export type Action<T extends ActionType> = {
type: T;
payload: ActionPayloads[T];
};

你的行动创造者现在可以根据地图来定义:

export function typedAction<T extends ActionType>(
type: T,
payload: ActionPayloads[T]
) {
return { type, payload };
}

接下来,您可以定义一个用于创建强类型reducer的辅助函数:

type ReducerMethods<State> = {
[K in ActionType]?: (state: State, payload: ActionPayloads[K]) => State
};
type Reducer<State> = (state: State, action: AppAction) => State;
function reducer<State>(
initialState: State,
methods: ReducerMethods<State>
): Reducer<State> {
return (state: State = initialState, action: AppAction) => {
const handler: any = methods[action.type];
return handler ? handler(state, action.payload) : state;
};
}

(我还没有找到一个很好的解决方法来解决那个丑陋的: any类型,但至少我们从逻辑上知道,键入是从外部发出的声音(。

现在,您可以通过为操作处理程序良好的隐式键入来定义您的reducers:

type TokenState = string | null;
export const tokenReducer = reducer<TokenState>(null, {
LOGIN: (state, token) => token, // `token` is implicitly typed as `string`
LOGOUT: () => null              // TS knows that the payload is `undefined`
});

相关内容

  • 没有找到相关文章

最新更新