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

TypeScript中奇怪的类型推断问题:payload被推断为never

问题解决:Reducer中Payload类型被推断为Never的处理方案

原因分析

你遇到的问题本质是TypeScript无法追踪type和payload之间的类型关联:

  • 从action中解构出type和payload后,两者变成了独立的联合类型
  • handlers[type]返回的是所有对应handler函数的联合类型,每个函数要求的payload类型各不相同
  • TypeScript无法确认当前type对应的payload正好匹配handler需要的类型,因此会将payload推断为所有可能payload类型的交集——也就是never

解决方法

方法1:类型断言快速修复

直接将payload断言为当前handler期望的参数类型,简单直接:

function reducer(state: CreateServerStateType, action: CreateServerActionType): CreateServerStateType {
  const { type, payload } = action;

  const handlers: CreateServerHandlersType = {
    INIT_INSTANCE_OPTIONS: (state, payload) => ({ ...state, instanceOptions: payload }),
    CHANGE_SPECS: (state, payload) => ({ ...state, selectedServerSpecs: payload }),
    CHANG_DATA_SIZE: (state, payload) => ({ ...state, dataSize: payload }),
    CHANGE_OS: (state, payload) => ({ ...state, selectedServerOS: payload }),
    CHANGE_PUBLIC_IP: (state, payload) => ({ ...state, publicIP: payload }),
    CHANGE_SERVER_NAME: (state, payload) => ({ ...state, serverName: payload }),
    CHANGE_SERVER_NUMBER: (state, payload) => ({ ...state, numberOfServers: payload }),
    CHANGE_USE_PASSWORD: (state, payload) => ({ ...state, usePassword: payload }),
    CHANGE_USE_SSL_KEY_PAIR: (state, payload) => ({ ...state, sslKeypair: payload }),
  };

  const handler = handlers[type];
  // 断言payload为当前handler的第二个参数类型
  return handler ? handler(state, payload as Parameters<typeof handler>[1]) : state;
}

方法2:使用Switch-Case实现类型窄化

这是TypeScript推荐的写法,通过switch-case自动窄化action的类型,让TypeScript准确推断payload类型:

function reducer(state: CreateServerStateType, action: CreateServerActionType): CreateServerStateType {
  switch (action.type) {
    case "INIT_INSTANCE_OPTIONS":
      return { ...state, instanceOptions: action.payload };
    case "CHANGE_OS":
      return { ...state, selectedServerOS: action.payload };
    case "CHANGE_SPECS":
      return { ...state, selectedServerSpecs: action.payload };
    case "CHANG_DATA_SIZE":
      return { ...state, dataSize: action.payload };
    case "CHANGE_PUBLIC_IP":
      return { ...state, publicIP: action.payload };
    case "CHANGE_USE_PASSWORD":
      return { ...state, usePassword: action.payload };
    case "CHANGE_USE_SSL_KEY_PAIR":
      return { ...state, sslKeypair: action.payload };
    case "CHANGE_SERVER_NAME":
      return { ...state, serverName: action.payload };
    case "CHANGE_SERVER_NUMBER":
      return { ...state, numberOfServers: action.payload };
    default:
      return state;
  }
}

方法3:封装泛型Reducer工具函数

如果想保留handler对象的写法,可以封装一个泛型工具函数,让TypeScript能正确关联type和action的类型:

// 泛型Reducer创建函数
function createReducer<TState, TAction extends { type: string }>(
  handlers: {
    [K in TAction['type']]: (state: TState, action: Extract<TAction, { type: K }>) => TState
  }
) {
  return (state: TState, action: TAction): TState => {
    const handler = handlers[action.type as keyof typeof handlers];
    return handler ? handler(state, action as Extract<TAction, { type: typeof action.type }>) : state;
  };
}

// 使用示例
const createServerReducer = createReducer<CreateServerStateType, CreateServerActionType>({
  INIT_INSTANCE_OPTIONS: (state, action) => ({ ...state, instanceOptions: action.payload }),
  CHANGE_OS: (state, action) => ({ ...state, selectedServerOS: action.payload }),
  CHANGE_SPECS: (state, action) => ({ ...state, selectedServerSpecs: action.payload }),
  CHANG_DATA_SIZE: (state, action) => ({ ...state, dataSize: action.payload }),
  CHANGE_PUBLIC_IP: (state, action) => ({ ...state, publicIP: action.payload }),
  CHANGE_USE_PASSWORD: (state, action) => ({ ...state, usePassword: action.payload }),
  CHANGE_USE_SSL_KEY_PAIR: (state, action) => ({ ...state, sslKeypair: action.payload }),
  CHANGE_SERVER_NAME: (state, action) => ({ ...state, serverName: action.payload }),
  CHANGE_SERVER_NUMBER: (state, action) => ({ ...state, numberOfServers: action.payload }),
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 12:59:13