TypeScript未检测到对象与数组的类型不匹配,原因何在?
为啥数组能赋值给字符串索引类型?
JS里数组其实就是特殊的对象:
- 数组的数字索引会自动转成字符串(比如
arr[0]和arr["0"]是一回事) - 数组本身带了
length、toString这些字符串键的属性,完全符合{ [key: string]: any }的定义
TypeScript的类型检查是基于结构兼容的,只要结构匹配就允许赋值,所以数组能被赋值给这个类型,泛型函数里也能通过约束检查。
怎么写只接受普通对象的函数?
要严格排除数组,只接受普通字符串键对象,有这几种办法:
方法1:用条件类型在泛型里拦数组
通过条件类型判断传入的是不是数组,是的话就返回never,直接在编译阶段报错:
type PlainObject = Record<string, any>; const fooFunction = <T extends PlainObject>(input: T extends any[] ? never : T) => { // 你的代码逻辑 }; fooFunction([]); // ❌ 编译报错:数组类型没法赋值给never fooFunction({ name: "test", age: 20 }); // ✅ 正常通过
方法2:编译约束加运行时检查
如果需要同时在运行时也防一手,可以加Array.isArray的检查,配合类型守卫:
const fooFunction = <T extends Record<string, any>>(input: T) => { if (Array.isArray(input)) { throw new Error("别传数组,传普通对象!"); } // 到这里input已经被推断成非数组对象了,放心写逻辑 }; fooFunction([]); // 编译不报错,但运行会抛错 fooFunction({ a: 1 }); // ✅ 编译运行都没问题
方法3:靠类型交集排除带length的对象
如果你的业务里普通对象不会用length属性,可以强制length为never,数组必然有length,自然就被排除了:
type PlainObject = Record<string, any> & { length?: never }; const fooFunction = <T extends PlainObject>(input: T) => { // 代码逻辑 }; fooFunction([]); // ❌ 编译报错:数组的length不符合never类型 fooFunction({ a: 1 }); // ✅ 正常通过 fooFunction({ a: 1, length: 10 }); // ❌ 也会报错,因为带了length属性
注意:方法3会把所有带length的普通对象也拦下来,要是你允许对象有length,优先用方法1。
内容的提问来源于stack exchange,提问作者BurnAsIce
相关产品推荐
相关产品推荐

