TS 对数组字面量的类型推断有时会出乎意料:const arr = [1, 2] 被推断为 number[] 而不是 [number, number],空数组 [] 被推断为 never[]。这些推断在 useState 初始化、函数返回数组、常量枚举等场景中经常引发类型错误。
// 数组类型推断的常见坑
// 坑一:字面量数组推断为宽泛类型
const arr1 = [1, 2, 3]; // 推断为 number[]
const arr2 = ['a', 'b']; // 推断为 string[]
const arr3 = [1, 'a']; // 推断为 (string | number)[]
// 如果你期望的是固定长度元组,需要 as const 或显式声明
const tuple1 = [1, 2] as const; // 推断为 readonly [1, 2]
const tuple2: [number, number] = [1, 2]; // 显式元组类型
// 坑二:空数组推断为 never[]
const empty = []; // 推断为 never[]
// empty.push(1); // 错误!never[] 不能 push number
// 解决:显式声明类型
const emptyNum: number[] = [];
const emptyStr: string[] = [];
// 坑三:React useState 初始值类型推断
// const [list, setList] = useState([]); // list 推断为 never[]
// setList([1, 2, 3]); // 错误!不能将 number[] 赋值给 never[]
// 解决:给 useState 加泛型
// const [list, setList] = useState<number[]>([]);
// 坑四:函数返回数组的类型推断
function getPair() {
return [1, 2]; // 推断为 number[],不是 [number, number]
}
const pair = getPair();
// pair[0] 是 number,但 pair 可以 push(3),不是元组
// 解决:显式声明返回类型
function getPairFixed(): [number, number] {
return [1, 2];
}
// 坑五:as const 让数组变只读元组
const roles = ['admin', 'user', 'guest'] as const;
// 推断为 readonly ["admin", "user", "guest"]
type Role = typeof roles[number]; // "admin" | "user" | "guest"
// roles.push('manager'); // 错误!只读数组不能修改
number[]),因为大多数场景下你需要的是可变数组而非固定元组。[] 没有元素可以推断,TS 只能推断为 never[]。必须显式标注类型。useState([]) 是最常见的踩坑场景,务必加上泛型 useState<ItemType[]>([])。as const 会将数组变为只读元组,如果后续需要 push/pop 等操作,要用显式类型注解。// 最佳实践:数组推断的各种正确姿势
// 1. 普通数组 -- 不需要特殊处理
const nums: number[] = [1, 2, 3];
// 2. 固定元组 -- 显式声明
const point: [number, number] = [10, 20];
// 3. 字面量枚举 -- as const
const colors = ['red', 'green', 'blue'] as const;
type Color = typeof colors[number]; // "red" | "green" | "blue"
// 4. 空数组初始化 -- 显式类型
const items: string[] = [];
items.push('hello'); // OK
// 5. 混合类型数组 -- 联合类型
const mixed: (string | number)[] = [1, 'two', 3];
// 6. React useState 正确写法
// const [users, setUsers] = useState<User[]>([]);
// const [status, setStatus] = useState<'idle' | 'loading' | 'error'>('idle');