TypeScript合并多对象时,如何检测重复键并抛出错误?
合并对象时检测重复键并触发TypeScript错误
现有两个对象:
obj A
const A = { resetPassword: "/supplier/auth/reset-password", login: "/supplier/auth/login", };
obj B
const B = { selfInitiate: "/supplier/actions/request", getEmpanelmentReport: "/supplier/empanelment/report", getFormData: "/supplier/empanelment/form/", formSubmit: "/supplier/empanelment/submit/", login: "duplicate login" };
需求是将这两个对象合并为一个所有键均为只读的对象C,目标类型定义如下:
type SUPPLIER_ROUTE = { readonly selfInitiate: string; readonly getEmpanelmentReport: string; readonly getFormData: string; readonly formSubmit: string; readonly resetPassword: string; readonly login: string; };
但直接使用扩展运算符合并时:
const C: SUPPLIER_ROUTE = { ...A, ...B };
TypeScript不会因为login键重复而抛出错误,需要实现合并时若存在重复键则触发类型错误的逻辑。
实现方案
1. 定义重叠键检测类型
创建一个类型工具NoOverlap,当两个类型存在共同键时返回never(触发类型错误),否则返回两者的交叉类型:
type NoOverlap<T, U> = keyof T & keyof U extends never ? T & U : never;
2. 锁定对象的字面量类型
默认TypeScript会把对象键推断为宽泛的string类型,需要用as const保留具体的键名和值类型,确保类型检测能精准识别重复键:
const A = { resetPassword: "/supplier/auth/reset-password", login: "/supplier/auth/login", } as const; const B = { selfInitiate: "/supplier/actions/request", getEmpanelmentReport: "/supplier/empanelment/report", getFormData: "/supplier/empanelment/form/", formSubmit: "/supplier/empanelment/submit/", login: "duplicate login" } as const;
3. 创建带类型检测的合并函数
编写一个泛型函数mergeNoOverlap,在参数层面限制第二个对象不能与第一个对象有重复键,同时返回只读的合并结果:
function mergeNoOverlap<T, U>( a: T, b: U extends NoOverlap<T, U> ? U : never ): Readonly<T & U> { return { ...a, ...b } as Readonly<T & U>; }
4. 验证效果
调用该函数合并A和B时,因为存在重复的login键,TypeScript会立即抛出类型错误:
// 类型错误:"{ readonly selfInitiate: "/supplier/actions/request"; ... }" 无法赋值给类型 "never" const C = mergeNoOverlap(A, B);
当移除B中的login键后,合并操作会正常通过,且返回的C是只读对象:
const B = { selfInitiate: "/supplier/actions/request", getEmpanelmentReport: "/supplier/empanelment/report", getFormData: "/supplier/empanelment/form/", formSubmit: "/supplier/empanelment/submit/", } as const; // 合并成功,C为只读的合并对象 const C = mergeNoOverlap(A, B);
可选:绑定目标类型SUPPLIER_ROUTE
如果需要确保合并结果严格符合SUPPLIER_ROUTE,可以在函数返回值上添加额外约束:
function mergeNoOverlap<T, U>( a: T, b: U extends NoOverlap<T, U> ? U : never ): Readonly<T & U> extends SUPPLIER_ROUTE ? Readonly<T & U> : never { return { ...a, ...b } as Readonly<T & U>; }
这样既会检测重复键,也能保证最终对象的结构与目标类型一致。
内容的提问来源于stack exchange,提问作者Arijit
相关产品推荐
相关产品推荐

