TypeScript中同类型数组推导的新数组为何类型不一致?
TypeScript数组取值构造新数组时类型推导不一致问题
问题现象
两个类型均为(0 | 1 | 2)[]的数组,取元素构造新数组时,得到的类型推导结果不同:一个保留了字面量联合类型(0 | 1 | 2)[],另一个被加宽为number[]。
复现代码如下:
// 显式标注类型:const nums: (0 | 1 | 2)[] const nums: (0 | 1 | 2)[] = []; // 推导结果:let tuple: (0 | 1 | 2)[] let tuple = [nums[0], nums[1]]; // 隐式推导类型,鼠标悬浮提示为const nums2: (0 | 1 | 2)[] const nums2 = ([] as {num?: 1 | 2}[]). map(n => typeof n.num === "undefined" ? 0 : n.num) // 推导结果:let tuple2: number[] let tuple2 = [nums2[0], nums2[1]];
根本原因
类型推导结果差异来自TypeScript的字面量类型加宽规则:
- 第一个
nums是显式标注了(0 | 1 | 2)[]类型,索引访问得到的元素类型固定为0 | 1 | 2,构造新数组时不会触发加宽,因此tuple类型正确。 - 第二个
nums2的类型是TS通过Array.prototype.map的泛型返回值自动推导的,没有显式类型标注。TS对这类隐式推导得到的泛型返回值,在后续做索引访问时,会默认将字面量类型加宽为对应的原始类型:即0 | 1 | 2被加宽为number,因此两个元素都是number类型,最终构造出的tuple2就成了number[]。
修复方案
可以任选以下一种方式解决:
- 显式给
nums2标注类型,从根源避免隐式推导带来的加宽问题
const nums2: (0 | 1 | 2)[] = ([] as {num?: 1 | 2}[]). map(n => typeof n.num === "undefined" ? 0 : n.num) // 此时tuple2推导结果为(0 | 1 | 2)[] let tuple2 = [nums2[0], nums2[1]];
- 调用
map时显式传入泛型参数,固定回调返回值类型
const nums2 = ([] as {num?: 1 | 2}[]). map<0 | 1 | 2>(n => typeof n.num === "undefined" ? 0 : n.num) let tuple2 = [nums2[0], nums2[1]];
- 给回调返回值加
as const断言,阻止字面量类型加宽
const nums2 = ([] as {num?: 1 | 2}[]). map(n => (typeof n.num === "undefined" ? 0 : n.num) as const) let tuple2 = [nums2[0], nums2[1]];
内容的提问来源于stack exchange,提问作者thisisrandy
相关产品推荐
相关产品推荐

