我正在尝试实现类型安全的功能,以基于“动作处理程序”映射创建化简器。想法是要有如下所示的API:
export const Actions = {
setToken: (token: string) => createAction(SET_TOKEN_TYPE, token),
invalidateToken: () => createAction(INVALIDATE_TOKEN_TYPE),
startLogin: () => createAction(START_LOGIN_TYPE)
};
export const reducer = createReducer<State, typeof Actions>(
{
[SET_TOKEN_TYPE]: ({ loginError, ...state }, action) => ({
...state,
token: action.payload,
loading: false
}),
[INVALIDATE_TOKEN_TYPE]: ({ token, ...state }) => state,
[START_LOGIN_TYPE]: ({ loginError, ...state }) => ({
...state,
loading: true
})
},
{
loading: false
}
);
createReducer
函数应该(为了清晰起见没有Typescript)看起来像这样:
function createReducer(handlers, initialState) {
return (state = initialState, action) => {
if (action.type in handlers) {
return handlers[action.type](state, action);
}
return state;
};
}
我创建了具有类型安全性的此类键入函数:
interface Action<T extends string> {
type: T;
}
type ActionCreator<T extends string> = (...args: any) => Action<T>;
type ActionsCreators = {
[creator: string]: ActionCreator<any>;
};
type ActionsUnion<Actions extends ActionsCreators> = ReturnType<
Actions[keyof Actions]
>;
type ActionHandlers<ActionCreators extends ActionsCreators, State> = {
[K in ReturnType<ActionCreators[keyof ActionCreators]>["type"]]: (
state: State,
action: ReturnType<ActionCreators[K]>
) => State
};
function createReducer<State, Actions extends ActionsCreators>(
handlers: ActionHandlers<Actions, State>,
initialState: State
) {
return (
state: State = initialState,
action: ActionsUnion<Actions>
): State => {
if (action.type in handlers) {
// unfortunately action.type is here any :(
return handlers[action.type](state, action); // here I have the error
}
return state;
};
}
在handlers[action.type]
中,我遇到了错误(noImplicitAny: true
)
元素隐式地具有“ any”类型,因为类型“ ActionHandlers”没有索引签名。
您知道如何在reducer内键入action.type
吗?
您可以在the gist
中找到整个示例答案 0 :(得分:0)
出现错误的原因是action.type
隐式键入为any
,因为没有应用任何适用的类型。
在链中的某个点上,您将any
用作需要扎根于string
的内容的类型参数:
type ActionsCreators = {
[creator: string]: ActionCreator<any>;
};
如果在此处添加类型参数,则可以替换any
;但是,您将需要将其一直传递下去。
请参阅以下版本进行了此更新。我不得不将某些中间类型重命名为通用名称(T
或P
),因为我很难保持键入的笔直。
有了额外的类型参数<P>
,我们现在有了以下类型,而不是隐式的any
:
const f = handlers[action.type];
在这里,f
变成ActionHandlers<P, T, State>[P]
export interface Action<T extends string> {
type: T;
}
export interface ActionWithPayload<T extends string, P> extends Action<T> {
payload: P;
}
export type ActionCreator<T extends string> = (...args: any) => Action<T>;
export type ActionsCreators<T extends string> = {
[creator: string]: ActionCreator<T>;
};
export type ActionsUnion<P extends string, T extends ActionsCreators<P>> = ReturnType<T[keyof T]>;
export type ActionHandlers<P extends string, T extends ActionsCreators<P>, State> = {
[K in ReturnType<T[keyof T]>["type"]]: (
state: State,
action: ReturnType<T[K]>
) => State
};
export function createReducer<P extends string, State, T extends ActionsCreators<P>>(
handlers: ActionHandlers<P, T, State>,
initialState: State
) {
return (state: State = initialState, action: ActionsUnion<P, T>): State => {
if (action.type in handlers) {
const f = handlers[action.type];
return f(state, action);
}
return state;
};
}