Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/visual-studio-2012/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
当给定联合类型时,为什么TypeScript错误地计算我的条件类型?_Typescript - Fatal编程技术网

当给定联合类型时,为什么TypeScript错误地计算我的条件类型?

当给定联合类型时,为什么TypeScript错误地计算我的条件类型?,typescript,Typescript,我通过阅读和了解了TypeScript中的条件类型,我认为我在TypeScript中发现了一个长期存在的bug 例如,此代码生成类型({value:string}{value:number})[,而我希望它生成类型({value:string | number})[ //给定一个数组,创建一个新数组,用于装箱字符串或 //将元素编号为具有“value”属性的新对象 声明函数boxup(arr:T[]):数组; 让myArray=[“你好”,42]; //“结果”具有如上所述的意外类型 let r

我通过阅读和了解了TypeScript中的条件类型,我认为我在TypeScript中发现了一个长期存在的bug

例如,此代码生成类型
({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[]
    ,这是空数组的类型
解释3:非匹配元素上的过滤器 第三种是这样的,元素按其类型过滤,我们返回:

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
    从不[]
这是上面列表中的解释2。但是如果我们想要其他的行为呢

如果你想要解释1,你应该写以下内容:

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[]
总之,根据代码的实际行为,您需要编写不同的类型来准确描述该行为。TypeScript将始终如一地将这些不同的类型解释为不同的含义;这种基于您编写的不同代码的一致解释是TypeScript如何让您描述
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;