既然nums
和nums2
都是类型(0 | 1 | 2)[]
,为什么tuple
和tuple2
有不同的类型?
// 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]];
游乐场
这是在microsoft/TypeScript#11126中实现的加宽和非加宽literal类型之间差异的结果。这令人困惑是可以理解的,因为IntelliSense并没有以任何不同的方式显示这些类型;它是该类型的一个不可见属性。例如,无法判断显示为0 | 1 | 2
的类型是否正在加宽。
一般来说,仅出现在表达式中(而不是类型)的文字类型在可以重新分配值的地方(例如非readonly
数组或元组的元素)推断时,将自动扩展到其相应的基元类型(因此0
将变为number
)。因此:
const nums = ([] as { num?: 1 | 2 }[]).map(n => typeof n.num === "undefined" ? 0 : n.num)
// const nums: (0 | 1 | 2)[]
nums
的类型是加宽文字类型的数组,因为类型0
来自表达式0
,类型1 | 2
来自表达式n.num
,但在map()
方法调用中,没有任何类型被显式写入类型。因此,当在可以重新分配的地方推断时,0 | 1 | 2
扩展为number
,例如数组文字:
let tuple = [nums[0], nums[1]];
// let tuple: number[]
另一方面,显式出现在类型中的文字类型(而不仅仅是表达式*)在类似情况下不会自动加宽。因此:
const nums: (0 | 1 | 2)[] = [];
// const nums: (0 | 1 | 2)[];
nums
的类型是非加宽文字类型的数组,因为类型0 | 1 | 2
来自nums
的类型注释。因此,当在一个可以重新分配的地方推断时,0 | 1 | 2
不会扩展到number
,例如数组文字:
let tuple = [nums[0], nums[1]];
// let tuple: (0 | 1 | 2)[];
如果希望加宽文字类型变为非加宽类型,可以在某个位置添加显式类型作为提示。类似于在表达式中显式键入其中一个值:
const nums = ([] as { num?: 1 | 2 }[]).map(n => typeof n.num === "undefined" ? 0 as 0 : n.num)
// const nums: (0 | 1 | 2)[]
let tuple = [nums[0], nums[1]];
// let tuple: (0 | 1 | 2)[];
或明确指定调用CCD_ 23 的类型参数
const nums = ([] as { num?: 1 | 2 }[]).map<0 | 1 | 2>(n => typeof n.num === "undefined" ? 0 : n.num)
// const nums: (0 | 1 | 2)[]
let tuple = [nums[0], nums[1]];
// let tuple: (0 | 1 | 2)[];
或在microsoft/TypeScript#12267内的本评论中讨论的任何方法。
游乐场链接到代码
我认为TypeScript读取:
let partOfSomeNums2 = [someNums2[1], someNums2[0]];
像
let partOfSomeNums2 = [0, 1];
在第二行中:partOfSomeNums2
是number[]
,就像第一行一样。
您可以使用:
let partOfSomeNums2 = [...someNums2.slice(1, 2), ...someNums2.slice(0, 1)];
或者:
let partOfSomeNums2: (0 | 1 | 2)[] = [someNums2[1], someNums2[0]];
我认为你的操场上的someWorkingFunction
是有效的,因为类型0 | 1 | 2
是直接从枚举中提取的,这与你的不工作代码相反,其中值0
是硬编码的。
编辑
我会使用这个解决方案:游乐场
使用此类型:type SmallNumber = 0 | 1 | 2;
然后,在映射函数中使用这种类型:
const someNums2 = someObjects2.map((obj): SmallNumber =>
typeof obj.num === "undefined" ? 0 : obj.num
);