DeepMerge <>泛型不支持深度可选

时间:2019-02-11 17:18:14

标签: typescript

我下面有一个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,
    },
  },
};

我想覆盖较深的address并添加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,
    },
  },
};

它应该要求line1line2zipburger

enter image description here

  

键入'{line1:string;第2行:字符串; zip:字符串;汉堡:真实; }'不能分配给类型'{burger:boolean; }'。     对象文字只能指定已知的属性,而'line1'在类型'{burger:boolean; }'。

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

1 个答案:

答案 0 :(得分:3)

嗯,我想这里的问题是keyof ({a: string} | undefined)显示为never,因此只要该属性是可选的,DeepMerge就无法递归下去。可能还有其他方法可以解决此问题,但是首先,如何将TU更改为自己的NonNullable版本,其中NonNullable是{{3} }

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;
}

编辑:您可能只想defined in the standard library,就像这样:

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;

也许对您来说更好?

希望有帮助。祝你好运!