TypeScript中如何仅复制泛型类型别名的Data字段?
TypeScript泛型中仅备份/还原Data类型字段的解决方案
问题背景
现有如下TypeScript泛型类型别名:
export type Editable<Data extends object> = { _backup: Data canRefresh: boolean canEdit: boolean canSave: boolean canCancel: boolean canDelete: boolean canUndelete: boolean /* 其他被省略的字段 */ } & Data
需要编写函数,仅将Data的字段复制到_backup或从_backup恢复,不处理Editable<Data>新增的额外字段。尝试的代码出现编译错误:
function backup<Data extends object>(editable: Editable<Data>): void { if (!editable._backup) editable._backup = {} as Data let key: keyof Data for (key in editable) editable._backup[key] = editable[key] } function restore<Data extends object>(editable: Editable<Data>): void { if (editable._backup) { let key: keyof Data for (key in editable) editable[key] = editable._backup[key] } }
问题核心:
- 原代码的
for...in会遍历Editable<Data>的所有字段(包括新增的额外字段),不符合需求 - TypeScript类型检查报错:
editable的键包含Data之外的字段,直接赋值给_backup(Data类型)会有类型不匹配
解决方案
方案1:利用类型断言限定遍历范围
由于Editable<Data>是Data与额外字段的交叉类型,可将editable断言为Data类型,仅遍历Data的字段,同时用hasOwnProperty确保只处理对象自有属性:
function backup<Data extends object>(editable: Editable<Data>): void { if (!editable._backup) { editable._backup = {} as Data; } // 将editable断言为Data类型,遍历其自有属性 const data = editable as Data; for (const key in data) { if (Object.prototype.hasOwnProperty.call(data, key)) { editable._backup[key] = data[key]; } } } function restore<Data extends object>(editable: Editable<Data>): void { if (editable._backup) { const data = editable as Data; for (const key in editable._backup) { if (Object.prototype.hasOwnProperty.call(editable._backup, key)) { data[key] = editable._backup[key]; } } } }
方案2:显式排除额外字段
如果Editable的额外字段是固定的,可以显式列出并排除,确保只处理Data的字段:
// 定义Editable的额外字段键集合 const EDITABLE_EXTRA_KEYS = new Set([ "_backup", "canRefresh", "canEdit", "canSave", "canCancel", "canDelete", "canUndelete" // 补充其他被省略的额外字段 ]); function backup<Data extends object>(editable: Editable<Data>): void { if (!editable._backup) { editable._backup = {} as Data; } for (const key in editable) { // 跳过Editable的额外字段 if (!EDITABLE_EXTRA_KEYS.has(key)) { const dataKey = key as keyof Data; editable._backup[dataKey] = editable[dataKey]; } } } function restore<Data extends object>(editable: Editable<Data>): void { if (editable._backup) { for (const key in editable._backup) { if (Object.prototype.hasOwnProperty.call(editable._backup, key)) { const dataKey = key as keyof Data; editable[dataKey] = editable._backup[dataKey]; } } } }
方案说明
- 两种方案都避免了遍历
Editable的额外字段,严格只处理Data类型的字段 - 方案1更简洁,依赖
Data与Editable额外字段无重叠的前提(若有重叠会导致类型冲突,需提前避免) - 方案2更直观,适合需要明确控制排除字段的场景,新增额外字段时只需更新
EDITABLE_EXTRA_KEYS集合
内容的提问来源于stack exchange,提问作者bikeman868
相关产品推荐
相关产品推荐

