TypeScript中keyof赋值及自定义MyOmit导致可选属性变必填原因咨询
TypeScript中keyof遍历后可选属性变必填的问题解答
错误产生原因
keyof操作符返回的仅为属性名的纯联合类型,不会携带原属性的可选/必填、readonly修饰符信息:keyof Props得到的就是"a" | "b" | "c" | "d",没有任何标记标识a/b/c为可选属性。- TypeScript的映射类型仅在
[P in keyof T]这种直接遍历源类型key的场景下,会自动读取并保留原属性的修饰符;如果in后面跟随的是提前定义的类型别名、或是Exclude/Extract等类型运算返回的联合类型,TS无法关联到源类型的属性修饰符,默认会将所有映射出的属性设为必填,就会出现可选属性异常变为必填的问题。 - 内置
Omit没有该问题的原因是TS官方对其做了修饰符保留的兼容处理,底层通过键重映射直接遍历源类型的key,完整保留了原属性的修饰符属性。
解决方案
场景1:提前将keyof结果赋值给类型别名后做映射
不要直接遍历类型别名生成映射类型,改用内置Pick工具类型实现,它会自动保留原属性的修饰符:
type Keys = keyof Props; // Pick会自动继承Props的属性修饰符 type Test1 = Pick<Props, Keys> const a1: Test1 = { d: '1' } // 运行正常无报错
场景2:自定义MyOmit工具类型
有两种兼容写法可选,都可以保留原属性的修饰符:
- 键重映射写法(适用于TS4.1及以上版本)
type MyOmit<T, K extends keyof any> = { [P in keyof T as Exclude<P, K>]: T[P] }
- 组合Pick和Exclude写法(全版本兼容)
type MyOmit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
修改后使用自定义MyOmit就不会再报可选属性缺失的错误:
const o1: MyOmit<Props, 'b' | 'c'> = { d: '1' } // 运行正常
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

