如何在TypeScript中区分ArrayLike与{length: number}类型?
区分TypeScript中的ArrayLike与仅含length属性的类型
TypeScript的结构类型系统会把仅带length属性的类型(比如{length: number})判定为兼容ArrayLike<T>,这是因为ArrayLike的数字索引签名在类型检查时允许源类型不明确声明该签名。要严格区分这两类类型,可以通过检查目标类型是否实际拥有数字索引签名来实现:
实现代码
// 提取类型的数字索引对应的类型,没有数字索引则返回never type HasNumericIndex<T> = T extends { [n: number]: infer U } ? U : never; // 严格判断是否为ArrayLike类型 type IsStrictArrayLike<T> = T extends ArrayLike<infer U> ? HasNumericIndex<T> extends never ? false : true : false; // 测试验证 type test1 = IsStrictArrayLike<ArrayLike<number>>; // true,符合预期 type test2 = IsStrictArrayLike<{ length: number }>; // false,达成需求 type test3 = IsStrictArrayLike<string>; // true(string本身属于ArrayLike类型) type test4 = IsStrictArrayLike<{ length: number; 0: string }>; // true(包含数字索引) type test5 = IsStrictArrayLike<() => void>; // false(函数有length但无数字索引)
简单原理
HasNumericIndex<T>用来检测类型是否有数字索引:如果有,返回该索引对应的值类型;没有的话返回never。IsStrictArrayLike<T>先判断目标类型是否兼容ArrayLike,再通过HasNumericIndex<T>的结果是否为never,把仅带length的类型从ArrayLike兼容类型里排除出去。
这样就能解决你遇到的{length: number} extends ArrayLike<number>返回true的问题,实现严格的类型区分。
内容的提问来源于stack exchange,提问作者zedryas
相关产品推荐
相关产品推荐

