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
相关产品推荐
相关产品推荐

