neurosnap
neurosnap

Reputation: 5798

How can I properly type merging objects?

I would like to write a function that behaves similarly to the object spread operation in terms of the resulting type. I cannot figure out a good way to do the following:

const one = {
  a: 'a',
};

const two = {
  b: 'b',
};

const three = {
  c: 'c',
};

const result = {
 ...one,
 ...two,
 ...three,
};
// the type for `result` is an object containing all three
// { a: 'a', b: 'b', c: 'c' };

const result = combine(one, two, three);
// I want the same result by using a function
// the typing result is an intersection though:
// { a: 'a' } & { b: 'b' } & { c: 'c' };

function combine<A>(a: A): A;
function combine<A, B>(a: A, b: B): A & B;
function combine<A, B, C>(a: A, b: B, c: C): A & B & C;
function combine(...args: any[]): any {
  const newObj = {};
  for (const obj of args) {
    for (const key in obj) {
      newObj[key] = obj[key];
    }
  }
  return newObj;
}

Upvotes: 1

Views: 111

Answers (1)

jcalz
jcalz

Reputation: 327934

If you have an intersection of object types with statically known keys and would like to collapse that into an equivalent single object type, you can do it like this:

type MergeIntersections<T> = T extends infer O ? {[K in keyof O]: O[K]} : never;

I'm using type inference in conditional types to "copy" T into O, and then maps the properties from O in the type to a single object type. (The copying is not always necessary, but it doesn't hurt and sometimes it dissuades the compiler from leaving an interface or type name).

And you could use it like this:

declare function combine<A>(a: A): MergeIntersections<A>;
declare function combine<A, B>(a: A, b: B): MergeIntersections<A & B>;
declare function combine<A, B, C>(a: A, b: B, c: C): MergeIntersections<A & B & C>;

const result = combine(one, two, three);
// const result: {a: string; b: string; c: string}

Hope that helps; good luck!

Link to code

Upvotes: 2

Related Questions