ts-toolbelt: `Object.Merge` with `deep` doesn't work on arrays/lists

🐞 Bug Report

Is your feature request related to a problem?

I’m trying to create a type from a deep merge of various types. When these types include arrays the merge stops working. I’ve tried Object.Assign, Object.AssignUp, Object.Merge, Object.MergeUp, and Object.Compact, none of them worked for my issue.

Describe the solution you’d like

Here’s a simplified example:

Given these types:

type Person = {
  type: "Person";
  name: string;
};

type Pet = {
  type: "Pet";
  kind: "dog" | "cat";
};

type HasPet = {
  pet: Pet;
};

I want to create a new type:

type Merged = {
  people: {
    type: "Person";
    name: string;
    pet: {
      type: "Pet";
      kind: "dog" | "cat";
    };
  }[];
}

This is what I tried:

import { O } from "ts-toolbelt";

type Merged = O.Compact<
  {},
  [
    {
      people: Person[];
    },
    {
      people: HasPet[];
    }
  ],
  "deep"
>;

I also tried replacing Compact with the aforementioned alternative types, to no avail.

This example, and another one both exist in a CodeSandbox: https://codesandbox.io/s/pensive-franklin-3xn39, if you want to play around with it.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 42 (30 by maintainers)

Most upvoted comments

You could have a CreativeGroupWithInstanceDataList that is a tuple instead of an union, this way you can have a list like you wanted. You’d need to reorganize the way your type is built from the deepest field up to its top parent.

I will also add an option so that CompactUp and AssignUp can be used with lodash merging style.

I see the new Compute/MergeUp are causing problems, so I will fix this very soon as well. But it’s almost there. Writing such types is actually very complex, so please excuse me for the bugs!

Will get back to you soon.

Busy looking into it

I’m pushing another update for A.Compute to help you vizualize your types more easily.

I will be glad to help you with a more complex example. Here’s how your first example would work:

import {A, O} from 'ts-toolbelt'

type Person = {
    type: 'Person';
    name: string;
};

type Pet = {
    type: 'Pet';
    kind: 'dog' | 'cat';
};

type HasPet = {
    pet: Pet;
};

type test0 = A.Compute<O.MergeUp<Person[], HasPet[], 'deep', 0>>

The Up versions are the couterparts that are able to deal with unions and optional fields. In essence MergeUp will behave like the spread operator while Merge will only merge fields that do not exist already. This might disappear in the upcoming v7 to make the Up versions the default.

At this moment, all utilities ending by Up use MergeUp under the hood. I also updated the docs for AssignUp, CompactUp,Assign and Compact to make it clearer. They all use Merge or MergeUp to merge batches (lists) of types.