不启用strictNullChecks时,如何将接口可空属性设为可选?
问题描述
假设有如下接口:
interface iUser { id: string; username: string; password: string; phone: string | null; age: number | null; }
需要实现一个泛型NullableOptional<T>,将接口中所有**显式声明为可空(包含null联合类型)**的属性设为可选,但保留其可赋值为null的特性,最终得到:
type CreateUser = NullableOptional<iUser>; // 等价于: type CreateUser = { id: string; username: string; password: string; phone?: string | null; age?: number | null }
此前参考的方案依赖strictNullChecks配置,代码如下:
type PickNullable<T> = { [P in keyof T as null extends T[P] ? P : never]: T[P] } type PickNotNullable<T> = { [P in keyof T as null extends T[P] ? never : P]: T[P] } type OptionalNullable<T> = { [K in keyof PickNullable<T>]?: Exclude<T[K], null> } & { [K in keyof PickNotNullable<T>]: T[K] }
但在未启用strictNullChecks时,null extends T[P]会对所有属性返回true(该模式下所有类型都隐式包含null/undefined),导致PickNullable<T>等于原类型,PickNotNullable<T>为空对象,无法满足需求。
解决方案(不启用strictNullChecks)
核心思路是通过类型标注的字面信息识别显式可空属性,而非依赖类型系统的null兼容性判断。利用TS的条件类型实现:
// 辅助类型:判断类型是否显式包含null联合 type IsExplicitNullable<T> = T extends null ? true : T extends infer U | null ? true : false; // 实现NullableOptional泛型 type NullableOptional<T> = // 保留非显式可空属性的必选性 { [K in keyof T as IsExplicitNullable<T[K]> extends false ? K : never]: T[K] } & // 将显式可空属性设为可选,保留原类型(包含null) { [K in keyof T as IsExplicitNullable<T[K]> extends true ? K : never]?: T[K] };
验证效果
将泛型应用到iUser后,可正常满足需求:
type CreateUser = NullableOptional<iUser>; // 合法赋值示例 const validUser1: CreateUser = { id: "1", username: "foo", password: "bar" }; const validUser2: CreateUser = { id: "1", username: "foo", password: "bar", phone: null }; const validUser3: CreateUser = { id: "1", username: "foo", password: "bar", age: 20 };
原理说明
IsExplicitNullable<T>通过两层条件判断:先识别纯null类型,再识别X | null的联合类型,精准定位显式声明的可空属性。- 泛型通过两个映射类型分别处理非可空和显式可空属性,再通过交叉类型合并结果,既保留了非可空属性的必选性,又保留了可空属性的
null赋值能力。
内容的提问来源于stack exchange,提问作者Dillon Brock
相关产品推荐
相关产品推荐

