162

I'd like to know the best way to do this, say I have two objects

var objectA = {
    propertyA: 1,
    propertyB: 2
    ...
    propertyM: 13
}

var objectB = {
    propertyN: 14,
    propertyO: 15
    ...
    propertyZ: 26
}

If objectC is created by

var objectC = Object.assign(objectA, objectB);

How can I declare/describe objectC, so the compiler/IDE knows that it has the properties of both objectA and objectB?

I'd like to find a way without the need of defining interfaces for objectA and objectB. I don't want to write declaration and definition/evaluation for the same property twice. This redundancy is significant if I have too many properties on an object.

(Is there an operator that can extract the interface/type of an existing object?)

Is it possible?

7 Answers 7

298

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); 
Sign up to request clarification or add additional context in comments.

7 Comments

just to improve the answer: this line does the trick: var objectC = {...objectA, ...objectB};
I want to update property of objectA if same key exist in both object. Means if key is same then it should take property of objectB. How is it possible?
Read the comments in my answer. Basically it constructs properties from left to right, where each new information overrides the existing one: var objectC = {...objectA, ...objectB};. Properties form objectB will override properties that were already assigned from objectA, if properties have the same name.
This works, therefore answers OP, however my concern is there is no type associated now to objectD or objectC , when you try to explicitly type it to something (assume there is such a type), it wont work. Is there type safe way of doing this ?
|
19

(Is there an operator that can extract the interface/type of an existing object? Is it possible?)

You should go for typeof.

type typeA = typeof objectA;
type typeB = typeof objectB;

To get them merged you can use intersection operation as basarat already pointed out.

type typeC = typeA & typeB

Comments

15

try this..

const person = { name: 'TRilok', gender: 'Male' };
const tools = { computer: 'Mac', editor: 'Atom' };
const attributes = { handsomeness: 'Extreme', hair: 'Brown', eyes: 'Blue' };

const summary = {...person, ...tools, ...attributes};

Comments

12

so the compiler/IDE knows that it has the properties of both objectA and objectB?

Use an intersection type + generics. E.g. from here

/**
 * Quick and dirty shallow extend
 */
export function extend<A>(a: A): A;
export function extend<A, B>(a: A, b: B): A & B;
export function extend<A, B, C>(a: A, b: B, c: C): A & B & C;
export function extend<A, B, C, D>(a: A, b: B, c: C, d: D): A & B & C & D;
export function extend(...args: any[]): any {
    const newObj = {};
    for (const obj of args) {
        for (const key in obj) {
            //copy all the fields
            newObj[key] = obj[key];
        }
    }
    return newObj;
};

More

Both are mentioned here : https://basarat.gitbooks.io/typescript/content/docs/types/type-system.html

2 Comments

Thanks. This seems to work. However the extend() function is defined in a 3rd party library, is there any way to overwrite this specific definition for extend() in its d.ts file? (I am using underscore _.extend() ).
Yup. Just modify underscore.d.ts
7

Use Typescript spread operator it transpile to Javascript Object.assign()

If you need deep tree object merging you could use changing function of best-global package

1 Comment

the spread operator is from JS not TS.
4

Lodash has an "extend" function that combines objects and lets Typescirpt know that the new object has the type you'd expect.

const a = { one: 1, two: 2 };
const b = { three: 3, four: 4 };
const c = lodash.extend(a, b);
// c: { one: 1, two: 2, three: 3, four: 4 }

1 Comment

Nice answer, thank you. "extend" of Lodash is better than merging via spread operator, because it takes properties (getters) into account!
0
var apparel = [{
    "ctaText": "Apparel BTN",
    "formBlurb": "<ul><li>Apparel tracking and allergen management</li><li>Expiration tracking for waste reduction</li><li>Collaborative forecasting and versatile supply chain management</li></ul>",
    "formImage": "",
    "formHeading": "Fill out this form for apparel information.",
    "marketoFormId": "2902",
    "showModalSide": false,
    "viewModalBackground": "dark",
    "formImageTopPosition": "",
    "successSubmissionCta": "Apparel Submit",
    "successSubmissionMessage": "Thank you for choosing Apparel"
  }];

  var food = [{
      "ctaText": "Food BTN",
      "formBlurb": "<h4>Test blurb</h4><u>underline this text</u><ul><li>fashion</li><li>textiles</li><li>upholstery</li></ul>",
      "formImage": "https://images.net/card-food-erp-for-quality-harvest-foods.jpg",
      "formHeading": "Fill out this form for food information.",
      "marketoFormId": "1086",
      "showModalSide": true
    }];


    if (
      Object.keys(apparel).length !=
      Object.keys(food).length
    ) {
      const initialDataFound = {...apparel, ...food };
      console.log(this.initialDataFound);
    }

Final result concates both apparel and food objects

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.