Vue3+TypeScript项目中Vuex动态设置对象属性如何保留类型安全
问题根因
类型失效的核心原因是:带泛型的mutation函数在被Parameters工具类推导参数类型时,泛型参数会被拉伸到最宽的约束范围,导致propName和对应属性值的绑定关系丢失,最终value会被推导为所有Order属性值的联合类型,自然不会触发类型错误。
解决方案
1. 修复当前Order属性设置的类型校验
调整Mutations的定义,不要将泛型写在mutation函数内部,而是提前枚举所有合法的payload联合类型,保证propName和value的绑定关系不丢失:
// 定义通用属性键值对生成工具 export type PropPayload<T> = { [K in keyof T]: { propName: K; value: T[K]; } }[keyof T]; // 调整Mutations定义 export type Mutations<S = State> = { [MutationTypes.setMetaProp]: ( state: S, payload: PropPayload<Order> ) => void; };
此时PropPayload<Order>会自动生成所有合法的payload联合,比如{propName: 'is_quote', value: boolean} | {propName: 'order_id', value: string},调用commit时传入value: '123'对应is_quote属性就会触发TS错误。
2. 封装为通用泛型支持多类型、嵌套属性设置
可以扩展工具类型,实现支持不同根对象、甚至深层子属性的类型校验:
// 仅支持一级属性的通用版本,适合单独设置某个根对象 type SimplePropPayload<T> = { [K in keyof T]: { propName: K, value: T[K] } }[keyof T]; // 支持深层嵌套属性的版本,可直接校验类似'order.address.city'的路径 type DeepPropPayload<T, Prefix extends string = ''> = { [K in keyof T & string]: T[K] extends object ? DeepPropPayload<T[K], `${Prefix}${K}.`> | { propName: `${Prefix}${K}`, value: T[K] } : { propName: `${Prefix}${K}`, value: T[K] } }[keyof T & string]; // 扩展Mutations支持多对象设置 export type Mutations<S = State> = { // 单独设置Order属性 [MutationTypes.setOrderProp]: (state: S, payload: SimplePropPayload<Order>) => void; // 单独设置User属性 [MutationTypes.setUserProp]: (state: S, payload: SimplePropPayload<User>) => void; // 全Store嵌套属性设置 [MutationTypes.setNestedProp]: (state: S, payload: DeepPropPayload<S>) => void; };
对应的通用mutation实现:
// 嵌套属性赋值工具函数 const setNestedValue = (obj: any, path: string, value: any) => { const keys = path.split('.'); const lastKey = keys.pop()!; const target = keys.reduce((cur, key) => cur?.[key], obj); target && (target[lastKey] = value); }; export const mutations: MutationTree<State> & Mutations = { [MutationTypes.setNestedProp](state, payload) { setNestedValue(state, payload.propName, payload.value); }, [MutationTypes.setOrderProp](state, {propName, value}) { state.order && (state.order[propName] = value); } };
关于TS+Vuex适配性的说明
该需求完全可以被满足,只是Vuex原生的类型系统没有对动态泛型mutation做特殊适配,只需要把泛型从mutation函数内部转移到类型生成阶段,提前展开所有合法payload的联合类型,就能实现完整的类型校验。
内容的提问来源于stack exchange,提问作者Komi
相关产品推荐
相关产品推荐

