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

TypeScript如何为索引签名的getter和setter分别定义不同类型

实现方案

前提要求

确保你使用的 TypeScript 版本 ≥ 4.3,该版本开始官方支持 getter 和 setter 定义不同类型,满足你读写分离的类型约束需求。

调整后的泛型类型

你可以直接替换原有的EditableObject定义,不需要修改配套的editableScope工具函数:

// 判断是否为普通对象,排除数组、日期、正则等特殊类型,可根据业务需求调整规则
type IsPlainObject<T> = T extends object
  ? T extends Array<any> | Function | Date | RegExp | Map<any, any> | Set<any>
    ? false
    : true
  : false;

export type EditableObject<T> = {
  // 读取属性时匹配该分支,返回非空类型
  [K in keyof T]: IsPlainObject<T[K]> extends true
    ? EditableObject<T[K]>
    : T[K]
// 写入属性时匹配该分支,允许传入undefined/null
} & {
  [K in keyof T]?: IsPlainObject<T[K]> extends true
    ? EditableObject<T[K]> | undefined | null
    : T[K] | undefined | null
}

验证效果

你可以用以下代码测试类型约束是否符合预期:

interface User {
  name: string;
  age: number;
  ext: {
    desc: string;
  }
}

const demoUser: User = {
  name: "测试",
  age: 20,
  ext: {
    desc: "测试描述"
  }
};

editableScope(demoUser, (editUser) => {
  // 写入允许空值,不会报错
  editUser.name = undefined;
  editUser.age = null;
  editUser.ext.desc = undefined;

  // 读取自动推导为非空类型,符合strictNullChecks要求
  const name = editUser.name; // 类型为string,无空类型联合
  const age = editUser.age; // 类型为number
  const desc = editUser.ext.desc; // 类型为string
});

实现原理

  • 利用 TypeScript 交叉类型的优先级特性,读取属性时会优先匹配更精确的非空定义分支,写入属性时会匹配更宽松的允许空值的可选分支
  • 内置普通对象判断逻辑,避免对数组、日期等特殊类型做递归处理,你也可以根据业务场景调整判断规则
  • 完全兼容原有工具函数逻辑,替换类型定义即可直接在现有项目中落地

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 18:09:01