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

React useReducer中Action类型定义引发TS2339错误排查

问题

在React的useReducer中使用formReducer,同时定义了基于联合类型的Action类型。当前遇到TS2339错误:在updated_input和updated_nested_input分支解构data时,提示'value'等属性不存在。尽管UpdatedInputActionData通过交叉类型(或interface扩展)继承了包含这些属性的StatePropObj,但仍报错,使用的TypeScript版本为^3.8.3。

reducer代码

export const formReducer = (state: FormState, action: Action) => {
  const { data, type } = action

  switch (type) {
    case 'updated_input': {
      const { value, hasError, error, name, isFormValid } = data
      return {
        ...state,
        [name]: {
          ...(state[name as keyof FormState] as {}),
          value,
          hasError,
          error,
        },
        isFormValid,
      }
    }
    case 'updated_nested_input': {
      const { value, hasError, error, isFormValid, nestedKey, name } = data
      return {
        ...state,
        [nestedKey]: {
          ...state[nestedKey],
          [name]: {
            ...(state[nestedKey][name as keyof (Address | AdminUser)] as {}),
            value,
            hasError,
            error,
            nestedKey,
          },
        },
        isFormValid,
      }
    }
    case 'focus_out_input': {
      const { name, isFormValid, touched, hasError, error } = data
      return {
        ...state,
        [name]: {
          ...(state[name as keyof FormState] as {}),
          touched,
          hasError,
          error,
        },
        isFormValid,
      }
    }
    case 'focus_out_nested_input': {
      const { touched, isFormValid, nestedKey, name, hasError, error } = data
      return {
        ...state,
        [nestedKey]: {
          ...state[nestedKey],
          [name]: {
            ...(state[nestedKey][name as keyof (Address | AdminUser)] as {}),
            touched,
            nestedKey,
            hasError,
            error,
          },
        },
        isFormValid,
      }
    }
    case 'submit_error': {
      const { submitErrors } = data
      return {
        ...state,
        submitErrors,
      }
    }

    default:
      throw new Error(`Unknown action type: ${type}`)
  }
}

Action类型定义

import { AxiosError } from 'axios'

export type NestedKey = 'address' | 'adminUser'
export interface StatePropObj {
  value: string
  error: string
  nestedKey: NestedKey | boolean
  touched: boolean
  hasError: boolean
}

export type UpdatedInputActionData = StatePropObj & {
  submitErrors?: AxiosError | boolean
  isFormValid: boolean
  name: string
}
export interface UpdatedNestedInputActionData extends UpdatedInputActionData {
  nestedKey: NestedKey
}


export type Action =
  | { type: 'updated_nested_input'; data: UpdatedInputActionData }
  | { type: 'submit_error'; data: { submitErrors: AxiosError | boolean } }
  | { type: 'updated_input'; data: UpdatedInputActionData }
  | {
      type: 'focus_out_input'
      data: {
        touched: boolean
        name: string
        isFormValid: boolean
        error: string
        hasError: boolean
      }
    }
  | {
      type: 'focus_out_nested_input'
      data: {
        touched: boolean
        name: string
        isFormValid: boolean
        nestedKey: NestedKey
        error: string
        hasError: boolean
      }
    }

错误信息示例

属性'value'不存在于类型'UpdatedInputActionData | { submitErrors: boolean | AxiosError; } | { touched: boolean; name: string; isFormValid: boolean; error: string; hasError: boolean; } | { ...; }'上。

49 const { value, hasError, error, name, isFormValid } = data

错误原因及解决方案

错误原因

  1. TypeScript 3.8版本的联合类型窄化局限性
    TypeScript 3.8对基于type字段的联合类型窄化支持不完善,尽管在switch分支中通过case指定了具体类型,但TS无法精准将action.data从Action联合类型中窄化为对应的UpdatedInputActionData,导致解构时认为data可能是联合类型中的其他成员,进而报错属性不存在。

  2. Action类型定义的精准度问题
    在Action联合类型中,updated_nested_input对应的data被错误定义为UpdatedInputActionData,而非更精准的UpdatedNestedInputActionData,这会加剧TS的类型推断混乱。

解决方案

方案1:升级TypeScript版本

TypeScript 4.0及以上版本对联合类型的控制流分析(基于switch case的类型窄化)做了大幅优化,升级到4.x版本后,TS能正确识别case分支中action.data的具体类型,错误会自动消失。

方案2:手动类型断言(适配TS 3.8)

若无法升级TS版本,可在分支中对data进行手动类型断言,明确告知TS当前分支的data类型:

case 'updated_input': {
  const { value, hasError, error, name, isFormValid } = data as UpdatedInputActionData
  // 后续代码不变
}
case 'updated_nested_input': {
  const { value, hasError, error, isFormValid, nestedKey, name } = data as UpdatedNestedInputActionData
  // 后续代码不变
}

方案3:修正Action类型定义(配合方案1或2)

将Action类型中updated_nested_input的data类型修正为UpdatedNestedInputActionData,让类型定义更精准:

export type Action =
  | { type: 'updated_nested_input'; data: UpdatedNestedInputActionData }
  | { type: 'submit_error'; data: { submitErrors: AxiosError | boolean } }
  | { type: 'updated_input'; data: UpdatedInputActionData }
  // 其他类型不变

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 21:15:05