我正在尝试创建一个类似于 Redux/Redux-toolkit 的小型系统。我想要的是有一个像这样的 createSlice 函数:
interface Lobby {
players: string[];
}
const slice = createSlice<Lobby>({
addPlayer: (state, name: string) => ({ ...state, players: [...players, name ] }),
});
它应该返回一个这样的对象:
{
actions: { addPlayer: (name: string) => ({ type: 'addPlayer', payload: name }) }
reducer: (state: Lobby, action: { type: string, payload: any }) => Lobby
}
经过试验,我认为 createSlice
不能以这种方式实现,因为它有多个泛型参数,您不能仅部分应用 state: Lobby
作为参数。
到目前为止,我最接近的是这个(实际实现除外):
const createSlice = <S, T extends Record<string, (state: S, payload: any) => S>>(
actions: T
) => {
type ActionsType = {
[K in keyof T]: (
payload: Parameters<T[K]>[1]
) => { type: K; payload: Parameters<T[K]>[1] };
};
type ReducerType = (state: S, { type: string, payload: any }) => S;
return {
actions: ("implementation" as any) as ActionsType,
reducer: ("implementation" as any) as ReducerType,
};
};
我可以这样使用:
const slice = createSlice({
addPlayer: (state: Lobby, name: string) => ({ ...state, players: [...players, name ] }),
});
这样可以,但是参数S
被错误(?)推断为unknown
,所以reducer类型错误,以下不会导致类型错误:
const actions = slice({
addPlayer: (state: Lobby, name: string) => 3, // 3 is not Lobby, so I want this to error
});
我现在不确定如何继续/解决这个问题..
答案 0 :(得分:1)
老实说,我并不完全理解是什么导致了最初的问题,但是将泛型类型分解出来允许 createSlice
使用单个泛型参数并且似乎可以正确地出错。
type ActionOptions<S> = Record<string, (state: S, payload: any) => S>;
type ActionsType<S, T extends ActionOptions<S>> = {
[K in keyof T]: (
payload: Parameters<T[K]>[1]
) => { type: K; payload: Parameters<T[K]>[1] };
};
type ReducerType<S> = (state: S, {type, payload}: { type: string, payload: any }) => S;
function createSlice<S> (
actions: ActionOptions<S>
) {
return {
actions: ("implementation" as any) as ActionsType<S, ActionOptions<S>>,
reducer: ("implementation" as any) as ReducerType<S>,
};
};
type Lobby = { __brand: 'lobby' };
const slice = createSlice({
addPlayer: (state: Lobby, payload: string) => ({ ...state, players: [payload ] }),
});
const actions = createSlice({
addPlayer: (state: Lobby, name: string) => 3, // Type 'number' is not assignable to type 'Lobby'
});