Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/8.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_Spread Syntax - Fatal编程技术网

Typescript 扩展运算符和可选字段。如何推断合适的类型

Typescript 扩展运算符和可选字段。如何推断合适的类型,typescript,spread-syntax,Typescript,Spread Syntax,假设您有一个具有不可为null的必填字段的对象: interface MyTypeRequired { value: number; } 您希望使用另一个对象的字段更新它,并使用可选字段: interface MyTypeOptional { value?: number; } 因此,您可以继续创建一个函数: function createObject(a: MyTypeRequired, b: MyTypeOptional) { return { ...a, .

假设您有一个具有不可为null的必填字段的对象:

interface MyTypeRequired { 
    value: number;
}
您希望使用另一个对象的字段更新它,并使用可选字段:

interface MyTypeOptional { 
    value?: number;
}
因此,您可以继续创建一个函数:

function createObject(a: MyTypeRequired, b: MyTypeOptional) {
    return { ...a, ...b };
}
type Spread<L, R> = Pick<L, Exclude<keyof L, keyof R>> & R;

function spread<T, U>(a: T, b: U): Spread<T, U> {
    return { ...a, ...b };
}

const t1_: { a: number; b: string }
const t2_: { a?: number; b: string }
const u1_: { a: number; c: boolean }
const u2_: { a?: number; c: boolean }

const t1u2 = spread(t1_, u2_); // { b: string; a?: number | undefined; c: boolean; }
const t2u1 = spread(t2_, u1_); // { b: string; a: number; c: boolean; }
此函数的推断返回类型是什么

const a = createObject({ value: 1 }, { value: undefined });
实验表明,它将符合MyTypeRequired接口,即使第二个排列有一个可选字段

如果我们更改顺序,则推断的类型不会更改,即使运行时类型会有所不同

function createObject(a: MyTypeRequired, b: MyTypeOptional) {
    return { ...b, ...a };
}

为什么TypeScript有这样的行为以及如何解决这个问题?

可以通过类型级别的函数来解决。考虑:

interface MyTypeRequired { 
    a: number;
    value: number;
}

interface MyTypeOptional { 
    b: string;
    value?: number;
}

type MergedWithOptional<T1, T2> = {
    [K in keyof T1]: K extends keyof T2 ? T2[K] extends undefined ? undefined : T2[K] : T1[K] 
} & {
    [K in Exclude<keyof T2, keyof T1>]: T2[K]
}

function createObject(a: MyTypeRequired, b: MyTypeOptional): MergedWithOptional<MyTypeRequired, MyTypeOptional> {
    return { ...b, ...a };
}
接口MyTypeRequired{
a:数字;
值:数字;
}
接口MyTypeOptional{
b:弦;
值?:数字;
}
类型MergedWithOptional={
[K in keyof T1]:K扩展T2的keyof?T2[K]扩展未定义?未定义:T2[K]:T1[K]
} & {
[K in Exclude]:T2[K]
}
函数createObject(a:MyTypeRequired,b:MyTypeOptional):MergedWithOptional{
返回{…b,…a};
}
为了检查行为,我添加了其他字段。我所做的是,当第二个对象有可选字段(可能<代码>未定义< /代码>)时,考虑将此添加到联合,因此结果将是<代码> t1[k]未定义的< /代码>。第二部分只是合并
T2
中但不在
T1
中的所有其他字段

更多详情:

  • K扩展T2的键?T2[K]扩展未定义?未定义:T2[K]:T1[K]
    如果第二个对象具有此键,并且其值可以未定义,则将未定义的追加到值类型,并将其追加而不替换,因为这是
  • 排除
    -仅使用不在第一个对象中的键

可以通过类型级别的功能来解决问题。考虑:

interface MyTypeRequired { 
    a: number;
    value: number;
}

interface MyTypeOptional { 
    b: string;
    value?: number;
}

type MergedWithOptional<T1, T2> = {
    [K in keyof T1]: K extends keyof T2 ? T2[K] extends undefined ? undefined : T2[K] : T1[K] 
} & {
    [K in Exclude<keyof T2, keyof T1>]: T2[K]
}

function createObject(a: MyTypeRequired, b: MyTypeOptional): MergedWithOptional<MyTypeRequired, MyTypeOptional> {
    return { ...b, ...a };
}
接口MyTypeRequired{
a:数字;
值:数字;
}
接口MyTypeOptional{
b:弦;
值?:数字;
}
类型MergedWithOptional={
[K in keyof T1]:K扩展T2的keyof?T2[K]扩展未定义?未定义:T2[K]:T1[K]
} & {
[K in Exclude]:T2[K]
}
函数createObject(a:MyTypeRequired,b:MyTypeOptional):MergedWithOptional{
返回{…b,…a};
}
为了检查行为,我添加了其他字段。我所做的是,当第二个对象有可选字段(可能<代码>未定义< /代码>)时,考虑将此添加到联合,因此结果将是<代码> t1[k]未定义的< /代码>。第二部分只是合并
T2
中但不在
T1
中的所有其他字段

更多详情:

  • K扩展T2的键?T2[K]扩展未定义?未定义:T2[K]:T1[K]
    如果第二个对象具有此键,并且其值可以未定义,则将未定义的追加到值类型,并将其追加而不替换,因为这是
  • 排除
    -仅使用不在第一个对象中的键

    • 警告!这是我对所发生事情的解释(理论)。还没有确定


      这个问题似乎是处理可选字段的预期行为

      定义此类型时:

      interface MyTypeOptional { 
          value?: number;
      }
      
      TypeScript期望值的类型为
      number | never

      所以当你传播它的时候

      const withOptional: MyTypeOptional = {};
      const spread = { ...withOptional };
      
      排列对象的类型应为
      {value:never | number}
      {value?:number}

      不幸的是,由于可选字段与未定义类型的用法不明确,脚本推断扩展对象如下:

      value?:数字|未定义

      因此,扩展时,可选字段类型被转换为
      number | undefined

      不合理的是,当我们传播非可选类型时,它似乎覆盖了可选类型:

      interface MyTypeNonOptional { 
          value: number;
      }
      const nonOptional = { value: 1 };
      const spread2 = { ...nonOptional, ...withOptional }
      
      看起来像只虫子?但不是,这里TypeScript不会将可选字段强制转换为
      number | undefined
      。在这里,它将其转换为
      number | never

      我们可以通过将可选更改为显式
      |未定义

      interface MyTypeOptional { 
          value: number | undefined;
      }
      
      const u2 = { a: undefined }
      const spread4 = { ...t1, ...u2 } // { a: undefined; }
      
      在这里,以下排列:

      const withOptional: MyTypeOptional = {} as MyTypeOptional;
      const nonOptional = { value: 1 };
      const spread2 = { ...nonOptional, ...withOptional }
      
      将被推断为
      {value:number |未定义;}

      当我们将其更改为可选或未定义时:

      interface MyTypeOptional { 
          value?: number | undefined;
      }
      
      它再次被破坏并推断为
      {value:number}


      那么,什么是解决办法

      我想说的是,在TypeScript团队修复之前不要使用可选字段

      这有几个含义:

      • 如果需要在未定义的情况下省略对象的键,请使用
        省略未定义(对象)
      • 如果需要使用Partial,请通过创建工厂来限制其使用:
        createPartial(o:Partial)=>Undefinable
        。因此,
        createPartial({})
        的结果将被推断为
        {value:undefined}
        undefineable
      • 如果需要将字段的值设置为
        unset
        ,请使用
        null
      关于可选字段的限制存在一个公开问题:
      警告!这是我对所发生事情的解释(理论)。还没有确定


      这个问题似乎是处理可选字段的预期行为

      定义此类型时:

      interface MyTypeOptional { 
          value?: number;
      }
      
      TypeScript期望值的类型为
      number | never

      所以当你传播它的时候

      const withOptional: MyTypeOptional = {};
      const spread = { ...withOptional };
      
      排列对象的类型应为
      {value:never | number}
      {value?:number}

      不幸的是,由于可选字段与未定义类型的用法不明确,脚本推断扩展对象如下:

      value?:数字|未定义

      因此,扩展时,可选字段类型被转换为
      number | undefined

      不合理的是,当我们传播非可选类型时,它似乎覆盖了可选类型:

      interface MyTypeNonOptional { 
          value: number;
      }
      const nonOptional = { value: 1 };
      const spread2 = { ...nonOptional, ...withOptional }
      
      看起来像只虫子?但不,这里TypeScript不播放op