如何在TypeScript中推断对象键数量及判断空对象?
TypeScript 推断对象键数量及空对象判断
问题场景
对于数组,我们可以通过索引访问length属性得到具体的长度类型:
type LengthArray<T extends readonly any[]> = T["length"]; type Length = LengthArray<["ryan", 1, true, 90]>; // Length 类型为 4
但对对象使用类似思路时,无法得到具体的键数量,只会返回number类型:
type LengthObject<T> = Array<keyof T>["length"]; type Length = LengthObject<{ name: string; age: number }>; // Length 类型为 number
最终需求是实现一个类型推断函数,判断对象是否无属性,返回对应的类型:
type LengthObject<T> = <?> function infer<T>(o: T): LengthObject<T> extends 0 ? number : string { // ... } const r1 = infer({}); // r1 应为 number const r2 = infer({ name: "ryan" }); // r2 应为 string
解决方案
方案1:直接判断空对象(无需计算键数量)
如果仅需判断对象是否为空,无需获取具体键数量,可以直接通过keyof T是否为never来判断——空对象的keyof结果是never,非空对象则为键的联合类型:
type IsEmptyObject<T> = keyof T extends never ? true : false; function infer<T>(o: T): IsEmptyObject<T> extends true ? number : string { return Object.keys(o).length === 0 ? (0 as unknown as number) : ('string' as unknown as string); } const r1 = infer({}); // 类型为 number const r2 = infer({ name: "ryan" }); // 类型为 string
方案2:获取对象的具体键数量
如果确实需要获取对象键的具体数量,需要通过递归将对象的键转换为字面量元组(只有字面量元组的length会返回具体数值类型):
// 递归将对象键转换为元组 type TupleFromKeys<T> = keyof T extends infer K ? K extends keyof T ? [K, ...TupleFromKeys<Omit<T, K>>] : [] : []; // 从元组获取长度 type LengthObject<T> = TupleFromKeys<T>["length"]; function infer<T>(o: T): LengthObject<T> extends 0 ? number : string { return Object.keys(o).length === 0 ? (0 as unknown as number) : ('string' as unknown as string); } const r1 = infer({}); // 类型为 number const r2 = infer({ name: "ryan" }); // 类型为 string
原理说明
- 数组的
length能返回具体数值,是因为字面量数组(如['a','b'])在TypeScript中会被推断为元组类型,元组的length是固定的字面量数值;而Array<keyof T>得到的是普通数组类型,其length属性类型为number。 - 递归转换元组的思路是每次取出对象的一个键,将其加入元组,再移除该键后递归处理剩余对象,最终得到包含所有键的字面量元组,其
length就是键的具体数量。
内容的提问来源于stack exchange,提问作者Ryan Izac
相关产品推荐
相关产品推荐

