TypeScript泛型问题:NonNull类型为何未排除null属性反而报错?
你的NonNull类型并没有移除属性,而是把属性的类型替换成了never,这就是编译器报错的核心原因。
原类型的实际作用
你编写的这个类型:
type NonNull<T> = { [P in keyof T]: T[P] extends undefined | null ? never : T[P]; }
它会遍历T的所有属性键,仅对属性值为null或undefined的键做类型替换——将其类型改为never,但属性键本身依然保留在类型结构中。
比如NonNull<{a: null, b: number}>的最终类型是:
{ a: never; b: number }
在TypeScript规则里,never类型没有任何有效值可以赋值,同时a依然是必填属性——你既没法给a赋值,又不能省略它,所以编译器会抛出“缺少属性a”的错误。
正确实现:移除null/undefined属性
要真正移除属性类型为null或undefined的键,需要用到TypeScript的键重映射(as子句),在遍历属性时直接过滤掉目标键:
type NonNull<T> = { [P in keyof T as T[P] extends null | undefined ? never : P]: T[P] };
当T[P]的类型是null或undefined时,as子句返回never,这个属性键会被直接排除出最终的类型。此时NonNull<{a: null, b: number}>的结果就是{b: number},赋值{ b: 12 }就完全符合类型要求了。
额外场景:保留属性但移除null/undefined类型
如果你想保留所有属性,只是把属性类型中的null和undefined去掉(比如处理{a: string | null}这种包含可选空值的类型),可以用Exclude工具类型:
type NonNullableProps<T> = { [P in keyof T]: Exclude<T[P], null | undefined> };
这个类型会把每个属性的类型中的null和undefined排除,比如NonNullableProps<{a: string | null, b: number}>的结果是{a: string; b: number},属性a依然存在,但类型不再包含null。
内容的提问来源于stack exchange,提问作者Michael Frysztacki

