为何自定义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
相关产品推荐
相关产品推荐

