当给定联合类型时,为什么TypeScript错误地计算我的条件类型?
我通过阅读和了解了TypeScript中的条件类型,我认为我在TypeScript中发现了一个长期存在的bug 例如,此代码生成类型当给定联合类型时,为什么TypeScript错误地计算我的条件类型?,typescript,Typescript,我通过阅读和了解了TypeScript中的条件类型,我认为我在TypeScript中发现了一个长期存在的bug 例如,此代码生成类型({value:string}{value:number})[,而我希望它生成类型({value:string | number})[ //给定一个数组,创建一个新数组,用于装箱字符串或 //将元素编号为具有“value”属性的新对象 声明函数boxup(arr:T[]):数组; 让myArray=[“你好”,42]; //“结果”具有如上所述的意外类型 let r
({value:string}{value:number})[
,而我希望它生成类型({value:string | number})[
//给定一个数组,创建一个新数组,用于装箱字符串或
//将元素编号为具有“value”属性的新对象
声明函数boxup(arr:T[]):数组;
让myArray=[“你好”,42];
//“结果”具有如上所述的意外类型
let result=boxup(myArray);
为什么TypeScript在这里做了错误的事情?有几种方法可以想象
boxup
如何工作
解释1:抛出非匹配元素
第一种是这样的,整个数组映射到一个与原始数组形状相同的全新数组,原始数组只能包含字符串或数字
function boxup<T>(arr: T[]) {
const arr[] = [];
for (const el of arr) {
if (typeof el === "string" || typeof el === "number") {
arr.push({ value: el });
} else {
throw new Error("Wrong!")
}
}
return arr;
}
如果这就是boxup
的工作方式,那么不同调用的正确结果是:
应该是boxup([“hello”,42])
(两个不同的同质数组之一)({value:string})[]|({value:number})[
应该是boxup([1,1])
(输出数组具有来自输入数组的相应类型)({value:number})[
应该是boxup([1,true])
-布尔值被过滤掉({value:number})[
应该是boxup([true])
,这是空数组的类型never[]
function boxup<T>(arr: T[]) {
const arr[] = [];
for (const el of arr) {
if (typeof el === "string" || typeof el === "number") {
arr.push({ value: el });
}
}
return arr;
}
基于我们可能的输入类型进行测试
是BoxOutputX
({value:string}{value:number})[]
是BoxOutputX
({value:number})[
是BoxOutputX
({value:number})[
是BoxOutputX
从不[]
type BoxOutput1<T extends string | number> = Array<[T] extends [string | number] ? { value: T } : never>;
type BoxOutput3<T> = Array<{ value: T extends string | number ? T : never }>;
根据此定义,结果如下:
是预期的类型BoxOutput3
{value:string | number}[]
是预期的类型BoxOutput3
{value:number}[]
是预期的类型BoxOutput3
{value:number}[]
是预期的类型BoxOutput3
never[]
boxup
可能工作的多种不同方式
等等,为什么?
为什么会发生这样的事
在TypeScript中,如果条件类型直接对类型参数进行操作,则它是分布式的:
// Distributive - the thing directly to the left of 'extends' is exactly a type parameter (T)
type Foo<T> = T extends Bar ? T : string;
// Not distributive
type Baz<T> = { name: T } extends SomeType ? { lastName: T } : string;
//Distributive-直接位于“extends”左侧的正是一个类型参数(T)
类型Foo=T扩展条?T:弦;
//不可分配
类型Baz={name:T}扩展了某种类型?{lastName:T}:string;
当一个类型是分布式的时,任何联合类型的每个组成部分都将单独求值,结果将合并到一个新的联合中:
// Distributive
type NumbersOnly<T> = T extends number ? T : never;
// Distribution applied: X is 1 | 2
type X = NumbersOnly<1 | 2 | "foo" | "bar">
// Same as if you had written
type X = NumbersOnly<1> | NumbersOnly<2> | NumbersOnly<"foo"> | NumbersOnly<"bar">
//分布式
类型NumbersOnly=T扩展数字?T:从来没有;
//应用的分布:X为1 | 2
类型X=仅数字
//就像你写的一样
类型X=仅数字|仅数字|仅数字|仅数字
通常-可能80%的时间-分配是你想要的。另外20%的情况下,您可以通过任意方式违反“对类型参数直接操作”规则,使您的类型不具有分布性。通常,这是通过将类型参数及其检查类型包装在一个1元素元组类型中来实现的
// Distributive
type Foo1<T> = T extends Bar ? T : string;
// Nondistributive
type Foo2<T> = [T] extends [Bar] ? T : string;
//分布式
类型Foo1=T扩展条?T:弦;
//非分布的
类型Foo2=[T]扩展[Bar]?T:弦;
所有这些的确切含义都超出了StackOverflow问题的范围——您应该阅读文档和发行说明的后半部分——但要记住的两个简单规则是:
- 测试裸类型参数的条件类型是分布的
- 如果不希望分配,请在
[]
// Distributive - the thing directly to the left of 'extends' is exactly a type parameter (T)
type Foo<T> = T extends Bar ? T : string;
// Not distributive
type Baz<T> = { name: T } extends SomeType ? { lastName: T } : string;
// Distributive
type NumbersOnly<T> = T extends number ? T : never;
// Distribution applied: X is 1 | 2
type X = NumbersOnly<1 | 2 | "foo" | "bar">
// Same as if you had written
type X = NumbersOnly<1> | NumbersOnly<2> | NumbersOnly<"foo"> | NumbersOnly<"bar">
// Distributive
type Foo1<T> = T extends Bar ? T : string;
// Nondistributive
type Foo2<T> = [T] extends [Bar] ? T : string;