代码之家  ›  专栏  ›  技术社区  ›  ThomasReggi

DeepMerge<>Generic不支持Deep可选

  •  3
  • ThomasReggi  · 技术社区  · 6 年前

    我有一个 DeepMerge<> 下面不支持深度可选合并的泛型。

    type DeepMerge<T, U> = [T, U] extends [object, object] ?
      {
        [K in keyof (U & Pick<T, Exclude<keyof T, keyof U>>)]: (
          K extends keyof U ? (
            K extends keyof T ? DeepMerge<T[K], U[K]> : U[K]
          ) : (
            K extends keyof T ? T[K] : never
          )
        )
      } : U;
    

    我有一个 Person 深可选 address .

    type Person = {
      name: string,
      age: number,
      address: {
        line1: string,
        line2: string | null | number,
        zip: string | number,
        address?: {
          line1: string,
          line2: string | null | number,
          zip: string | number,
        },
      },
    };
    

    我想覆盖深 地址 并添加 burger .

    type PersonOverwrites = {
      address: {
        pizzaDelivery: boolean,
        address?: {
          burger: boolean,
        },
      },
    };
    

    这是抱怨:

    const person: DeepMerge<Person, PersonOverwrites> = {
      name: 'Thomas',
      age: 12,
      address: {
        line1: 'hi',
        line2: 'hi',
        zip: 'hi',
        pizzaDelivery: true,
        address: {
          line1: 'hi',
          line2: 'hi',
          zip: 'hi',
          burger: true,
        },
      },
    };
    

    它应该需要 line1 , line2 , zip , 汉堡 .

    enter image description here

    类型“line1:string;line2:string;zip:string;burger:true;”不能分配给类型“burger:boolean;”。 对象文本只能指定已知属性,类型“burger:boolean;”中不存在“line1”。

    它应该支持 { line1: string; line2: string; zip: string; burger: true; } 但是整个深度对象被覆盖到 { burger: boolean; } .

    1 回复  |  直到 6 年前
        1
  •  3
  •   ThomasReggi    6 年前

    嗯,我想问题是 keyof ({a: string} | undefined) 显示为 never 所以只要财产是可选的, DeepMerge 不能再向下移动了。可能还有其他方法可以解决这个问题,但是对于第一次拍摄,如何改变 T U NonNullable 他们自己的版本,其中 不可空的 defined in the standard library 作为

    type NonNullable<T> = T extends null | undefined ? never : T;
    

    让我们试试看:

    type DeepMerge<_T, _U, T= NonNullable<_T>, U= NonNullable<_U>> =
      [T, U] extends [object, object] ?
      {
        [K in keyof (U & Pick<T, Exclude<keyof T, keyof U>>)]: (
          K extends keyof U ? (
            K extends keyof T ? DeepMerge<T[K], U[K]> : U[K]
          ) : (
            K extends keyof T ? T[K] : never
          )
        )
      } : U;
    

    这行吗?推断出 person 作为

    const person: {
        address: {
            pizzaDelivery: boolean;
            address?: {
                burger: boolean;
                line1: string;
                line2: string | number | null;
                zip: string | number;
            } | undefined;
            line1: string;
            line2: string | number | null;
            zip: string | number;
        };
        name: string;
        age: number;
    }
    

    编辑:你想要的也可能是 distribute across all unions ,像这样:

    type DeepMerge<T, U> = T extends any ? U extends any ?
      [T, U] extends [object, object] ?
      {
        [K in keyof (U & Pick<T, Exclude<keyof T, keyof U>>)]: (
          K extends keyof U ? (
            K extends keyof T ? DeepMerge<T[K], U[K]> : U[K]
          ) : (
            K extends keyof T ? T[K] : never
          )
        )
      } : U : never : never;
    

    也许这对你更好?

    希望有帮助。祝你好运!