【问题标题】:Typescript: generic type not correctly inferred (`unknown`)打字稿:未正确推断泛型类型(`未知`)
【发布时间】:2021-05-06 20:01:39
【问题描述】:

我正在尝试创建一个类似于 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不能以这种方式实现,因为它有多个泛型参数,你不能只部分应用状态: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
});

我现在不确定如何继续/解决这个问题..

【问题讨论】:

  • 请考虑修改此处的代码以构成 minimal reproducible example 适合放入像 The TypeScript Playground 这样的独立 IDE 中,其中唯一存在的问题是您要询问的问题;任何错别字都应该更正,这样想要帮助的人就可以花时间寻找解决方案,而不必先“找到问题”。
  • this 是否满足您的需求,使用柯里化来模拟部分类型参数推断?如果不是,请详细说明。

标签: typescript generics types


【解决方案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'
});

playground

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-23
    • 2020-05-25
    • 2020-12-22
    • 1970-01-01
    • 2021-03-10
    • 2020-12-17
    相关资源
    最近更新 更多