如何减少类型转换并修复Object.keys.reduce的TypeScript类型错误
React表单状态转POST请求体的TypeScript类型错误解决
问题背景
我有一个React表单组件,会将表单值传入函数,把表单状态中的所有值格式化为POST请求体所需的结构,但当前代码存在TypeScript类型错误,无法正常编译。
最简可复现代码
import type { AxiosError } from 'axios'; export interface OrgAddress { line1: string postCode: string country: string city: string name: string comment: string } export type SecurityRole = 'superadmin' | 'protectedadmin' | 'admin' | 'manager' export interface User { id: string username: string firstName: string lastName: string securityRole: SecurityRole organisationId: Organisation['id'] } export type OrgAdminUser = Pick<User, 'firstName' | 'lastName' | 'username'> export interface Organisation { id: string name: string contactNumber: string category: string registeredCharityNumber: string address: OrgAddress adminUser: OrgAdminUser createdAt: string updatedAt: string } // 补全缺失的PostOrgBody类型,对应POST请求体结构 type PostOrgBody = Omit<Organisation, 'id' | 'createdAt' | 'updatedAt'> export type NestedKey = 'address' | 'adminUser' export interface StatePropObj { value: string error: string nestedKey: NestedKey | boolean touched: boolean hasError: boolean } export interface FormState { name: StatePropObj registeredCharityNumber: StatePropObj contactNumber: StatePropObj category: StatePropObj address: Address adminUser: AdminUser isFormValid: boolean submitErrors: AxiosError | false } export interface Address { line1: StatePropObj postCode: StatePropObj country: StatePropObj city: StatePropObj name: StatePropObj comment: StatePropObj nested: boolean } export interface AdminUser { firstName: StatePropObj lastName: StatePropObj email: StatePropObj // 注意:这里对应OrgAdminUser的username字段 nested: boolean } // 原存在类型错误的转换函数 const getFormPayload = (formState: FormState) => { return Object.keys(formState).reduce((acc, key: keyof typeof formState) => { const formStateProp = formState[key] if (typeof formStateProp === 'boolean') return acc if (formStateProp && 'nested' in formStateProp) { acc[key] = Object.keys(formStateProp).reduce( (nestedAcc, nestedKey: keyof typeof formStateProp) => { const prop = formStateProp[nestedKey] if (typeof prop === 'boolean') return nestedAcc if ('value' in prop) { nestedAcc[nestedKey] = prop.value return nestedAcc } return nestedAcc }, {} as OrgAddress | OrgAdminUser ) } if (formStateProp && 'value' in formStateProp) { acc[key] = formStateProp.value } return acc }, {} as PostOrgBody) }
核心困惑
reduce方法要返回PostOrgBody类型的对象,但遍历的是FormState的键。二者键名大部分重合,但TypeScript不允许用FormState的键直接给PostOrgBody类型的累加器赋值,同时嵌套结构的联合类型也导致推断模糊。
错误原因
- 键类型不精确:
Object.keys()返回的是string[],TypeScript无法确认这些字符串是PostOrgBody的有效键,直接赋值会触发类型不兼容错误。 - 嵌套类型推断模糊:嵌套
reduce的累加器用OrgAddress | OrgAdminUser联合类型,TypeScript无法确定当前处理的是哪种结构,导致赋值时类型校验失败。 - 字段名不匹配:
FormState中adminUser的email字段对应PostOrgBody里的username,原代码未处理这个映射,导致类型不匹配。
修复后的代码
const getFormPayload = (formState: FormState): PostOrgBody => { // 手动指定需要转换的键,排除不需要的表单状态字段 const transformableKeys: Array<keyof Omit<FormState, 'isFormValid' | 'submitErrors'>> = [ 'name', 'registeredCharityNumber', 'contactNumber', 'category', 'address', 'adminUser' ]; return transformableKeys.reduce((acc, key) => { const formProp = formState[key]; // 处理顶层非嵌套字段 if ('value' in formProp) { acc[key] = formProp.value as PostOrgBody[typeof key]; return acc; } // 处理嵌套字段:区分address和adminUser的类型 if ('nested' in formProp) { type TargetNestedType = typeof key extends 'address' ? OrgAddress : OrgAdminUser; const nestedPayload = Object.keys(formProp).reduce((nestedAcc, nestedKey) => { const nestedProp = formProp[nestedKey as keyof typeof formProp]; if ('value' in nestedProp) { // 处理adminUser的email映射到username的逻辑 const targetField = key === 'adminUser' && nestedKey === 'email' ? 'username' : nestedKey; (nestedAcc as Record<string, string>)[targetField] = nestedProp.value; } return nestedAcc; }, {} as TargetNestedType); acc[key] = nestedPayload as PostOrgBody[typeof key]; return acc; } return acc; }, {} as PostOrgBody); };
解决思路
- 精确控制遍历键:手动列出需要转换的键,避免遍历到
isFormValid、submitErrors这类不需要的字段,同时保证键的类型是PostOrgBody的有效键子集。 - 分支处理嵌套类型:根据外层键(
address/adminUser)分别指定嵌套累加器的具体类型,避免联合类型带来的推断模糊。 - 字段映射处理:针对
adminUser中email到username的字段名差异,手动添加映射逻辑,确保最终结构符合PostOrgBody要求。 - 类型断言辅助:在安全的场景下使用类型断言,帮助TypeScript完成类型推断,同时保证代码逻辑的正确性。
内容的提问来源于stack exchange,提问作者HelloWorld
相关产品推荐
相关产品推荐

