You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript 2.8+:如何捕获并映射Action Creator到Action Handler?

嘿,我来帮你搞定这个TypeScript泛型的问题,让你的action handler能自动精准推导action的类型~

首先,先回顾一下你已经定义好的action creator代码:

/** simply captures the string literal type */
function actionType<T extends string, U>(type: T, u: U) {
  return Object.assign({}, { type }, u);
}

// ------------------------------------
// Action Creators
// ------------------------------------
const ACTION_CREATORS = {
  toggleFilter: (params: { filterValueKey: string; filterKey: string; filterValue: string }) => {
    const { filterKey, filterValue, filterValueKey } = params;
    return actionType('FILTERS_TOGGLE', { payload: { filterValueKey, filterKey, filterValue } });
  },
  deleteFilter: (params: { filterValueKey: string }) => {
    const { filterValueKey } = params;
    return actionType('FILTERS_DELETE', { payload: { filterValueKey } });
  },
  searchFilterValues: (params: { activeFilterKey: string; query: string }) => {
    const { query, activeFilterKey } = params;
    return actionType('FILTERS_QUERY_VALUES', { payload: { query, activeFilterKey } });
  }
};

接下来我们来实现你需要的ActionHandlers泛型,核心思路是从action creators里提取每个action的具体类型,然后映射到对应的handler函数上:

第一步:定义辅助类型

先写几个基础的辅助类型,用来提取和转换action相关的类型:

// 从单个action creator函数中提取它返回的action类型
type ActionFromCreator<T> = T extends (...args: any[]) => infer Action ? Action : never;

// 将action creators对象映射为 { [creatorKey: string]: Action } 的类型
type ActionsMap<T> = {
  [K in keyof T]: ActionFromCreator<T[K]>;
};

// 提取所有action的type字段,得到联合类型(比如 "FILTERS_TOGGLE" | "FILTERS_DELETE" | ...)
type ActionTypes<T> = ActionsMap<T>[keyof T]['type'];

// 根据指定的action type,找到对应的具体action类型
type ActionForType<T, Type extends string> = Extract<ActionsMap<T>[keyof T], { type: Type }>;

第二步:实现核心的ActionHandlers泛型

基于上面的辅助类型,我们可以写出最终的ActionHandlers:

type ActionHandlers<T extends Record<string, (...args: any[]) => { type: string }>> = {
  // 遍历所有action type,为每个type生成对应的handler函数
  [Type in ActionTypes<T>]: (state: any, action: ActionForType<T, Type>) => any;
};

第三步:测试你的ACTION_HANDLERS

现在用这个泛型来标注你的handler对象,每个handler里的action参数都会自动推导出准确的类型,包括payload的属性:

const ACTION_HANDLERS: ActionHandlers<typeof ACTION_CREATORS> = {
  FILTERS_TOGGLE: (state, action) => {
    // 这里三个payload属性都会被正确推断为string类型
    action.payload.filterValueKey;
    action.payload.filterKey;
    action.payload.filterValue;
    const newState = { ...state };
    return newState;
  },
  FILTERS_DELETE: (state, action) => {
    // action.payload.filterValueKey 类型正确
    action.payload.filterValueKey;
    return state;
  },
  FILTERS_QUERY_VALUES: (state, action) => {
    // action.payload.query 和 activeFilterKey 类型正确
    action.payload.query;
    action.payload.activeFilterKey;
    const newState = { ...state };
    return newState;
  }
};

额外优化:给State也加上类型

如果你想让state参数也有强类型约束,可以给ActionHandlers再加一个泛型参数:

// 扩展后的泛型,支持指定State类型
type ActionHandlers<State, T extends Record<string, (...args: any[]) => { type: string }>> = {
  [Type in ActionTypes<T>]: (state: State, action: ActionForType<T, Type>) => State;
};

// 假设你的状态类型是这样的
type FilterState = {
  activeFilters: Record<string, string>;
  // 其他状态字段...
};

// 使用时指定State类型
const ACTION_HANDLERS: ActionHandlers<FilterState, typeof ACTION_CREATORS> = {
  FILTERS_TOGGLE: (state, action) => {
    // state现在有FilterState的类型约束
    const newState = { ...state };
    // 处理逻辑...
    return newState;
  },
  // 其他handler...
};

这样不仅action类型精准,state也会有严格的类型检查,而且handler必须返回和输入相同的State类型,避免类型不一致的问题。

内容的提问来源于stack exchange,提问作者Rico Kahler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:03:08