TypeScript类型映射中空对象类型检测及转换为void类型的实现问题问询
如何在TypeScript中检测空对象类型并映射为void
这个问题确实是TypeScript类型系统里的一个常见坑——你遇到的问题核心在于{}在TS中的行为和我们直觉里的“空对象”完全不同:几乎所有非null/undefined的类型都能extends {},包括字符串、数字这些原始类型(因为它们有对应的包装对象),更别说所有对象类型了,所以你的第一个写法会把绝大多数类型都映射成void,显然不符合预期。
而第二个写法T extends { any: any }确实只会匹配带有any属性的对象,完全没法覆盖“任意非空对象”的场景。
正确实现方案
要准确检测“空对象类型”,我们需要结合object类型判断和keyof操作符——空对象类型的核心特征是没有任何已知的属性键,也就是keyof T等于never。同时我们还要可以选择排除object这个通用对象类型(如果你希望它不被视为空对象的话)。
基础版本(包含object类型)
这个版本会把{}、Record<never, never>以及object类型都视为空对象:
// 辅助类型:判断是否为空对象 type IsEmptyObject<T> = // 先过滤出对象类型(排除原始类型、null、undefined) T extends object ? // 检查对象是否没有任何已知属性键 keyof T extends never ? true : false : false; // 最终类型工具:空对象映射为void,否则保留原类型 type NotEmpty<T> = IsEmptyObject<T> extends true ? void : T;
严格版本(排除object类型)
如果你希望object这个通用类型不被映射为void,可以调整辅助类型,额外排除object本身:
type IsStrictEmptyObject<T> = T extends object ? // 确保是{}的子类型、无已知属性,且不是object本身 (T extends {} ? keyof T extends never ? object extends T ? false : true : false : false) : false; type NotEmpty<T> = IsStrictEmptyObject<T> extends true ? void : T;
测试用例验证
我们可以用几个例子验证效果:
// 基础版本测试 type Test1 = NotEmpty<{}>; // void ✔️ type Test2 = NotEmpty<Record<never, never>>; // void ✔️ type Test3 = NotEmpty<{a: number}>; // {a: number} ✔️ type Test4 = NotEmpty<string>; // string ✔️ type Test5 = NotEmpty<null>; // null ✔️ type Test6 = NotEmpty<object>; // void(基础版本)/ object(严格版本) ✔️
原理说明
T extends object:先过滤掉原始类型、null和undefined,只处理对象类型。keyof T extends never:判断该对象类型是否没有任何已知的属性键——空对象类型的keyof结果就是never,而非空对象(比如{a: number})的keyof会返回它的属性名联合类型。object extends T:在严格版本中,这个判断用来排除object类型本身——因为object是所有对象的父类,object extends T只有当T是object时才会返回true,这样就能把它从“空对象”的判断中排除。
内容的提问来源于stack exchange,提问作者lokimidgard
相关产品推荐
相关产品推荐

