Seems like this should do the trick:

var objectA = {
    propertyA: 1,
    propertyB: 2,
    .
    . // more properties here
    .
    propertyM: 13
};

var objectB = {
    propertyN: 14,
    propertyO: 15,
    .
    . // more properties here
    .
    propertyZ: 26
};

var objectC = {...objectA, ...objectB}; // this is the answer

var a = objectC.propertyA;

var n = objectC.propertyN;

Based on this article: https://blog.mariusschulz.com/2016/12/23/typescript-2-1-object-rest-and-spread


In addition, the order of the variables in the decomposition matters. Consider the following:

var objectA = {
    propertyA: 1,
    propertyB: 2, // same property exists in objectB
    propertyC: 3
};

var objectB = {
    propertyX: 'a',
    propertyB: 'b', // same property exists in objectA
    propertyZ: 'c'
};

// objectB will override existing properties, with the same name,
// from the decomposition of objectA
var objectC = {...objectA, ...objectB}; 

// result: 'b'
console.log(objectC.propertyB); 

// objectA will override existing properties, with the same name,
// from the decomposition of objectB
var objectD = {...objectB, ...objectA}; 

// result: '2'
console.log(objectD.propertyB); 
Answer from alkasai on Stack Overflow
🌐
Omarileon
omarileon.me › blog › typescript-merge-objects
mari. | 3 Simple Ways to Merge Objects in TypeScript
January 26, 2024 - Instead of a merging all of the properties, you get a type union - they're essentially identical, but the second option retains the information about the separate types that were merged. So to sum things up, to merge objects in TypeScript, you can use either the spread syntax, Lodash's merge function, or Object.assign.
Discussions

How to merge two object TypeScript?
Communities for your favorite technologies. Explore all Collectives · Stack Overflow for Teams is now called Stack Internal. Bring the best of human thought and AI automation together at your work More on stackoverflow.com
🌐 stackoverflow.com
[HELP] How to join two objects of different types on matching keys?
It sounds like what you're trying to do is strip off—at runtime—any properties from objB that don't exist in TypeA. Fundamentally, you're going to need to write something to do the comparison and remove any unwanted fields. The reason is that types in TypeScript exist only at compile time, they are completely removed from the final JS output. So at runtime, there isn't any concept of TypeA any more, the objects are just JS objects. In order to do a runtime check, you'll need to encode the fields in some way that exists at runtime, then write a comparison that references that list of fields. More on reddit.com
🌐 r/typescript
6
3
May 4, 2021
How can I keep an optional property in a merged type where there is an overlap?
You have to Omit the property you want to replace for this to work More on reddit.com
🌐 r/typescript
9
10
August 16, 2022
Merging two objects, using the spread operator, allows the addition of properties not defined in an interface.
An interface is a minimum set of properties, so you can do it directly even without merge. interface HasColor { color: string } const car: Car = { color: 'white', make: 'Toyota', model: 'Supra' } const cat: Cat = { color: 'brown', name: 'Felix' } ... function isWhite(item:HasColor) { return item.color === 'white' } ... // both implement HasColor so both work const a = isWhite(cat); const a = isWhite(car); This is a good resource: https://basarat.gitbook.io/typescript/ More on reddit.com
🌐 r/typescript
10
10
April 30, 2021
Top answer
1 of 11
73

UPDATE for TS4.1+

The original answer still works (and you should read it if you need an explanation), but now that recursive conditional types are supported, we can write merge() with to be variadic:

type OptionalPropertyNames<T> =
  { [K in keyof T]-?: ({} extends { [P in K]: T[K] } ? K : never) }[keyof T];

type SpreadProperties<L, R, K extends keyof L & keyof R> =
  { [P in K]: L[P] | Exclude<R[P], undefined> };

type Id<T> = T extends infer U ? { [K in keyof U]: U[K] } : never

type SpreadTwo<L, R> = Id<
  & Pick<L, Exclude<keyof L, keyof R>>
  & Pick<R, Exclude<keyof R, OptionalPropertyNames<R>>>
  & Pick<R, Exclude<OptionalPropertyNames<R>, keyof L>>
  & SpreadProperties<L, R, OptionalPropertyNames<R> & keyof L>
>;

type Spread<A extends readonly [...any]> = A extends [infer L, ...infer R] ?
  SpreadTwo<L, Spread<R>> : unknown

type Foo = Spread<[{ a: string }, { a?: number }]>

function merge<A extends object[]>(...a: [...A]) {
  return Object.assign({}, ...a) as Spread<A>;
}

And you can test it:

const merged = merge(
  { a: 42 },
  { b: "foo", a: "bar" },
  { c: true, b: 123 }
);
/* const merged: {
    a: string;
    b: number;
    c: boolean;
} */

Playground link to code

ORIGINAL ANSWER


The intersection type produced by the TypeScript standard library definition of Object.assign() is an approximation that doesn't properly represent what happens if a later argument has a property with the same name as an earlier argument. Until very recently, though, this was the best you could do in TypeScript's type system.

Starting with the introduction of conditional types in TypeScript 2.8, however, there are closer approximations available to you. One such improvement is to use the type function Spread<L,R> defined here, like this:

// Names of properties in T with types that include undefined
type OptionalPropertyNames<T> =
  { [K in keyof T]: undefined extends T[K] ? K : never }[keyof T];

// Common properties from L and R with undefined in R[K] replaced by type in L[K]
type SpreadProperties<L, R, K extends keyof L & keyof R> =
  { [P in K]: L[P] | Exclude<R[P], undefined> };

type Id<T> = T extends infer U ? { [K in keyof U]: U[K] } : never // see note at bottom*

// Type of { ...L, ...R }
type Spread<L, R> = Id<
  // Properties in L that don't exist in R
  & Pick<L, Exclude<keyof L, keyof R>>
  // Properties in R with types that exclude undefined
  & Pick<R, Exclude<keyof R, OptionalPropertyNames<R>>>
  // Properties in R, with types that include undefined, that don't exist in L
  & Pick<R, Exclude<OptionalPropertyNames<R>, keyof L>>
  // Properties in R, with types that include undefined, that exist in L
  & SpreadProperties<L, R, OptionalPropertyNames<R> & keyof L>
  >;

(I've changed the linked definitions slightly; using Exclude from the standard library instead of Diff, and wrapping the Spread type with the no-op Id type to make the inspected type more tractable than a bunch of intersections).

Let's try it out:

function merge<A extends object, B extends object>(a: A, b: B) {
  return Object.assign({}, a, b) as Spread<A, B>;
}

const merged = merge({ a: 42 }, { b: "foo", a: "bar" });
// {a: string; b: string;} as desired

You can see that a in the output is now correctly recognized as a string instead of string & number. Yay!


But note that this is still an approximation:

  • Object.assign() only copies enumerable, own properties, and the type system doesn't give you any way to represent the enumerability and ownership of a property to filter on. Meaning that merge({},new Date()) will look like type Date to TypeScript, even though at runtime none of the Date methods will be copied over and the output is essentially {}. This is a hard limit for now.

  • Additionally, the definition of Spread doesn't really distinguish between missing properties and a property that is present with an undefined value. So merge({ a: 42}, {a: undefined}) is erroneously typed as {a: number} when it should be {a: undefined}. This can probably be fixed by redefining Spread, but I'm not 100% sure. And it might not be necessary for most users. (Edit: this can be fixed by redefining type OptionalPropertyNames<T> = { [K in keyof T]-?: ({} extends { [P in K]: T[K] } ? K : never) }[keyof T])

  • The type system can't do anything with properties it doesn't know about. declare const whoKnows: {}; const notGreat = merge({a: 42}, whoKnows); will have an output type of {a: number} at compile time, but if whoKnows happens to be {a: "bar"} (which is assignable to {}), then notGreat.a is a string at runtime but a number at compile time. Oops.

So be warned; the typing of Object.assign() as an intersection or a Spread<> is kind of a "best-effort" thing, and can lead you astray in edge cases.

Playground link to code


*Note: Id<T> is an identity type and in principle shouldn't do anything to the type. Someone at some point edited this answer to remove it and replace with just T. Such a change isn't incorrect, exactly, but it defeats the purpose... which is to iterate through the keys to eliminate intersections. Compare:

type Id<T> = T extends infer U ? { [K in keyof U]: U[K] } : never 

type Foo = { a: string } & { b: number };
type IdFoo = Id<Foo>; // {a: string, b: number }

If you inspect IdFoo you will see that the intersection has been eliminated and the two constituents have been merged into a single type. Again, there's no real difference between Foo and IdFoo in terms of assignability; it's just that the latter is easier to read in some circumstances.

2 of 11
15

Update:

Thanks to the comment from re-gor I revisited this and updated the syntax to be more explicit about the merge.

type Merge<A, B> = {
  [K in keyof A | keyof B]: 
    K extends keyof A & keyof B
    ? A[K] | B[K]
    : K extends keyof B
    ? B[K]
    : K extends keyof A
    ? A[K]
    : never;
};

By extending the keyof operator to A and B individually, all the keys are exposed.

Using nested ternary types, first check to see if the key is present in A and B, and when it is, then the type is A[K] | B[K].

Next, when the key only comes from B, then the type is B[K].

Next, when the key only comes from A, then the type is A[K].

Finally the key does not exist in neither A nor B and the type is never.

type X = Merge<{ foo: string; bar: string }, { bar: number }>;

>>> type X = { foo: string; bar: string | number; }

Previous:

I found a syntax to declare a type that merges all properties of any two objects.

type Merge<A, B> = { [K in keyof (A | B)]: K extends keyof B ? B[K] : A[K] };

This type allows you to specify any two objects, A and B.

From these, a mapped type whose keys are derived from available keys from either object is created. The keys come from keyof (A | B).

Each key is then mapped to the type of that key by looking up the appropriate type from the source. If the key comes from B, then the type is the type of that key from B. This is done with K extends keyof B ?. This part asks the question, "is K a key from B" ? To get the type of that key, K, use a property lookup B[K].

If the key is not from B, it must be from A, thus the ternary is completed:

K extends keyof B ? B[K] : A[K]

All of this is wrapped in an object notation { }, making this a mapped object type, whose keys are derived from two objects and whose types map to the source types.

🌐
Aaron Bos
aaronbos.dev › posts › merge-objects-typescript-javascript
Merging Objects in TypeScript (and JavaScript)
June 11, 2022 - The reason for this being is that shallow merging pertains to simply copying property values from one or many objects to another. If a property is a reference type like an object or array, then the reference will be copied instead of creating entirely new objects. The two most common methods of shallow merging in TS and JS are Object.assign() and using the ...
🌐
Tim Mousk
timmousk.com › blog › typescript-merge-objects
How To Merge Objects In TypeScript? – Tim Mouskhelichvili
March 27, 2023 - typescriptconst obj1 = { name: 'Tim' }; const obj2 = { role: 'Admin' }; const merged = Object.assign({}, obj1, obj2); // Outputs: { "name": "Tim", "role": "Admin" } console.log(merged);
🌐
Bobby Hadz
bobbyhadz.com › blog › typescript-merge-objects
How to merge Objects in TypeScript | bobbyhadz
Use the spread syntax (...) to merge objects in TypeScript. The type of the final object will successfully be inferred, so trying to add or remove properties causes the type checker to show an error.
Find elsewhere
🌐
Squash
squash.io › tutorial-on-merging-objects-in-typescript
How to Merge Objects in TypeScript - Squash Labs
May 8, 2024 - Merging two objects in TypeScript involves combining the properties of two objects into a new object. This can be achieved using the spread operator (...) or the Object.assign method. Here's an example of merging two objects using the spread ...
🌐
SPGuides
spguides.com › merge-two-objects-in-typescript
How to Merge Two Objects in TypeScript?
February 7, 2024 - In TypeScript, you can merge two objects by using the spread operator … to combine the properties of both objects into a new one. Alternatively, you can use Object.assign() to achieve the same result.
🌐
Medium
medium.com › @glasshost › how-to-merge-objects-in-typescript-52e2d64e77b6
How to merge Objects in TypeScript | by Glasshost | Medium
April 14, 2023 - The resulting merged object contains the properties from both objects. There are multiple ways to merge objects in TypeScript, including the spread operator, `Object.assign()`, and the Lodash `merge()` method.
🌐
TypeScript
typescriptlang.org › docs › handbook › declaration-merging.html
TypeScript: Documentation - Declaration Merging
TypeScript uses declaration merging to build up definitions like this in a type-safe way. ... Not all merges are allowed in TypeScript. Currently, classes can not merge with other classes or with variables. For information on mimicking class merging, see the Mixins in TypeScript section. Although JavaScript modules do not support merging, you can patch existing objects by importing and then updating them.
🌐
Webdevtutor
webdevtutor.net › blog › typescript-merge-two-objects-recursively
How to Merge Two Objects Recursively in TypeScript
In this tutorial, we will explore how to merge two objects recursively in TypeScript to combine their properties deeply. One way to merge two objects recursively is to create a function that iterates over the properties of both objects and merges them accordingly.
🌐
DEV Community
dev.to › techsquidtv › combine-objects-in-typescript-3f64
Combine Objects In TypeScript - DEV Community
April 11, 2021 - Our Hero has fallen into a vat of nuclear waste, what emerges will no longer be a Hero, but a SuperHero. ... interface Hero { class: "human" | "elf" | "orc", baseHealth: number } interface SuperPowers { flying?: boolean, superSpeed?: boolean, strengthMultiplier?: number } interface SuperHero extends Hero, SuperPowers {} const human: Hero = {class: "human", baseHealth: 100} const powers: SuperPowers = {flying: true} const superMan: SuperHero = {...human, ...powers} TypeScript Playground: TS Playground Spread Syntax: MDN Spread Syntax
🌐
DEV Community
dev.to › svehla › typescript-how-to-deep-merge-170c
How to deep merge in Typescript - DEV Community
October 5, 2021 - type Head<T> = T extends [infer I, ...infer _Rest] ? I : never type Tail<T> = T extends [infer _I, ...infer Rest] ? Rest : never type Zip_DeepMergeTwoTypes<T, U> = T extends [] ? U : U extends [] ? T : [ DeepMergeTwoTypes<Head<T>, Head<U>>, ...Zip_DeepMergeTwoTypes<Tail<T>, Tail<U>> ] /** * Take two objects T and U and create the new one with uniq keys for T a U objectI * helper generic for `DeepMergeTwoTypes` */ type GetObjDifferentKeys< T, U, T0 = Omit<T, keyof U> & Omit<U, keyof T>, T1 = { [K in keyof T0]: T0[K] } > = T1 /** * Take two objects T and U and create the new one with the same ob
🌐
DEV Community
dev.to › fullstackchris › advanced-typescript-a-generic-function-to-merge-object-arrays-18ja
Advanced TypeScript: A Generic Function to Merge Object Arrays - DEV Community
December 9, 2021 - You could explicitly write key / value assignments for the keys that you want to update... or you can leverage JavaScript's built in Object.assign function and TypeScript's generic capabilities to only write one such function for all merging actions you need across your entire app! 😄 · For example, in ReduxPlate, I have two types, IFile, and IEditorSettings: ... IEditorSettings extends IFile and has just one additional property: isActive. When visitors click the "Generate!" button on the MVP page, the response from the server returns an array of objects of type IFile instead of IEditorSettings, since the server is not concerned with the isActive property.
🌐
Sling Academy
slingacademy.com › home › typescript › how to merge multiple objects in typescript (2 approaches)
How to Merge Multiple Objects in TypeScript (2 Approaches) - Sling Academy
December 3, 2023 - If you hover your mouse over one of the results, you’ll see it type: This approach works almost the same as the previous one. ... const objectOne = { name: 'Product One', price: 100, description: 'This is product one', }; const objectTwo = { weight: 100, color: 'red', }; const objectThree = { name: 'Product Three', descriptioN: 'Some text', color: 'blue', }; // Merge objectOne and objectTwo by using Object.assign() const resultOne = Object.assign({}, objectOne, objectTwo); console.log(resultOne); // Merge objectOne, objectTwo, and objectThree by using Object.assign() const resultTwo = Object.assign({}, objectOne, objectTwo, objectThree); console.log(resultTwo);
🌐
xjavascript
xjavascript.com › blog › typescript-merge-two-objects
Merging Two Objects in TypeScript: A Comprehensive Guide — xjavascript.com
The spread operator (...) is a concise and popular way to merge objects in TypeScript. It allows you to expand the properties of an object into another object. // Define two objects const obj1 = { a: 1, b: 2 }; const obj2 = { b: 3, c: 4 }; // ...
🌐
Ultimate Courses
ultimatecourses.com › blog › use-intersection-types-to-combine-types-in-typescript
Use Intersection Types to Combine Types in TypeScript - Ultimate Courses
July 7, 2022 - There’s a concept called “Intersection Types” in TypeScript that essentially allows us to combine multiple types. Intersection types allow us to reuse existing type definitions from anywhere, and create new combinations of them.
🌐
YouTube
youtube.com › watch
Merge with Mike Poteat - TypeScript Type Challenges #599 ...
To learn more, please visit the YouTube Help Center: https://www.youtube.com/help
🌐
Stack Overflow
stackoverflow.com › questions › 62714020 › how-to-merge-two-object-typescript
How to merge two object TypeScript?
let a = [{ id: 1, selected: false, key: 'plan' }]; let b = [ { id: 1, selected: true, key: 'plan', text: 'aaaa' }, { id: 2, selected: true }, ]; const mergeArrays = (array1, array2) => { array2 = array2 || []; array1 = array1 || []; return Object.values([...array1, ...array2].reduce((result, obj) => { result = { ...result, [obj.id]: {...obj} } return result; }, {})) }; //Consider you got the response from server //a -> Original array, b -> Response from serer console.log(mergeArrays(a, b)) //Response from server is null or [] console.log(mergeArrays(a, null)) //original array is empty but there's a response from server console.log(mergeArrays([], b)) //Consider there is completely a different object in the array received from the server than the one defined in the original array let c = [{ id: 2, selected: true, key: 'plan' }]; console.log(mergeArrays(a, c))