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

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];
            }
        }
    }
}

方案说明

  1. 两种方案都避免了遍历Editable的额外字段,严格只处理Data类型的字段
  2. 方案1更简洁,依赖Data与Editable额外字段无重叠的前提(若有重叠会导致类型冲突,需提前避免)
  3. 方案2更直观,适合需要明确控制排除字段的场景,新增额外字段时只需更新EDITABLE_EXTRA_KEYS集合

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 04:47:19