如何在TypeScript中将指定属性标记为可选类型?
实现TypeScript泛型工具类型MakeOptional<T, U>
你说得对,之前的写法只能给属性类型加上undefined,但没办法真正把属性标记为可选(带?修饰符)。要同时实现这两点,我们可以通过拆分类型再合并的方式来解决:
优雅实现方案
直接利用TypeScript内置的Omit、Pick和Partial工具类型组合,代码非常简洁:
type MakeOptional<T, U extends keyof T> = Omit<T, U> & Partial<Pick<T, U>>;
分步解释
我们拆解一下这个类型的逻辑:
Omit<T, U>:从类型T中移除所有属于U的属性,得到一个只包含原类型中非指定属性的必选类型。比如对于你的interface A,Omit<A, 'foo'>会得到{ bar: number; baz: Date }。Partial<Pick<T, U>>:先通过Pick<T, U>从T中挑选出所有属于U的属性,再用Partial把这些属性全部转为可选类型。比如Partial<Pick<A, 'foo'>>会得到{ foo?: string }。- 交叉类型
&:把上面两个结果合并,就得到了“原属性除U外保持必选,U转为可选”的最终类型。
验证你的示例
用这个实现测试你给出的案例:
interface A { foo: string; bar: number; baz: Date; } type AWithOptionalFoo = MakeOptional<A, 'foo'>; // 结果:{ bar: number; baz: Date } & { foo?: string } // 合并后等价于:{ foo?: string; bar: number; baz: Date } // (开启strictNullChecks时,可选属性自动包含undefined,和你的预期一致) type AWithOptionalBarAndBaz = MakeOptional<A, 'foo' | 'baz'>; // 结果:{ bar: number } & { foo?: string; baz?: Date } // 合并后等价于:{ foo?: string; bar: number; baz?: Date }
为什么之前的写法不行?
你之前的代码{ [P in keyof T]: P extends U ? T[P] | undefined : T[P] }是在遍历所有属性时修改类型,但没有改变属性的可选性标记——遍历keyof T得到的属性默认都是必选的,哪怕类型里加了undefined,属性本身还是带required约束的,和真正的可选属性(带?)行为不同。
内容的提问来源于stack exchange,提问作者Rico Kahler
相关产品推荐
相关产品推荐

