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

为何自定义MyOmit与TypeScript内置Omit处理结果存在差异?

为什么自定义MyOmit和内置Omit处理可选属性结果不同?

先看代码示例:

type User = {
  name?: string;
  age?: number;
  sex?: string;
};
type MyOmit<T, K extends PropertyKey> = { [P in Exclude<keyof T, K>]: T[P] };

type A = MyOmit<User, "name" | "age">; // { sex: string | undefined }
type B = Omit<User, "name" | "age">; // { sex?: string | undefined }

二者结果差异的核心原因是是否保留原属性的可选修饰符(?):

1. 内置Omit的实现逻辑

TypeScript内置的Omit本质是Pick和Exclude的组合,定义如下:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

其中Pick<T, K>的作用是从类型T中选取指定键K对应的属性,关键在于:Pick在映射属性时,会完整保留原属性的所有修饰符(包括可选性?、只读readonly等)。因为K是keyof T的子集,TypeScript会识别这些键直接来自原类型的属性定义,因此保留其可选标记。

2. 自定义MyOmit的局限

你的MyOmit直接使用{ [P in Exclude<keyof T, K>]: T[P] }进行映射,问题出在:

  • Exclude<keyof T, K>返回的是经过运算后的字符串字面量联合类型,而非直接引用原类型的键集合
  • TypeScript处理这种非直接来自keyof T的映射时,不会保留原属性的?修饰符,只会提取属性的类型(可选属性的类型本身就包含undefined,比如sex?: string的类型是string | undefined)
  • 最终生成的是必选属性,只是类型允许undefined,所以结果是{ sex: string | undefined }

3. 让自定义MyOmit和内置Omit行为一致的方法

如果想让自定义的MyOmit保留可选修饰符,直接对齐内置逻辑,基于Pick实现即可:

type MyOmit<T, K extends PropertyKey> = Pick<T, Exclude<keyof T, K>>;

此时type A = MyOmit<User, "name" | "age">的结果就会和B完全一致:{ sex?: string | undefined }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 08:25:13