What is the purpose of Object.assign() in the constructor of a Typescript object?

12,799

Solution 1

This a method to easily add the values of the parameters of a class to their respective class fields where a class implements that interface or at least has a partial implantation of that interface.

interface IPerson {
  firtName: string;
  lastName: string;
}

class Person implements IPerson {
  public firtName!: string;
  public lastName!: string;

  constructor(params: IPerson) {
    Object.assign(this, params);
  }
}

Your application works because you seem to have implemented this in such a way that the callback value of values to also be enough.

The main issue with this Hack is that Object.assign is not type safe. So using it in this way in a way goes against the point of TypeScript.

If you want to do this in a type safe fashion you are better off using a custom implementation where the type is properly checked. Something like this:

type PDM = PropertyDescriptorMap;

export class ClassSAssign<T> {
  constructor(private objectToSpread: T, private klass: T) {}

  private propertyDescriptorOptions = {
    enumerable: true,
    writable: true
  };

  public apply(): void {
    const map = this.getPropertiesDescriptorMap();
    Object.defineProperties(this.klass, map);
  }

  private getPropertiesDescriptorMap(): PDM {
    return Object.entries(this.objectToSpread).reduce(
      (obj: PDM, entry) => this.getPropertyDescriptorMap(obj, entry),
      {}
    );
  }

  private getPropertyDescriptorMap(obj: PDM, [key, value]: [string, any]): PDM {
    return {
      ...obj,
      [key]: {
        value,
        ...this.propertyDescriptorOptions
      }
    };
  }
}

and you can use this utility like this:

class Person implements IPerson {
  public firtName!: string;
  public lastName!: string;

  constructor(params: IPerson) {
    new ClassSAssign(params, this).apply();
  }
}

If you don't/can't want to use the above, I suggest you at least add some type rigour to protect your class from what values can be passed into it

interface IToDo {
  id?: number;
  title?: string;
}

export class Todo implements IToDo {
  public id?: number;
  public title?: string;
  public complete: boolean = false;
  public editMode: boolean = false;

  constructor(values?: IToDo) {
    Object.assign(this, values);
  }
}

Solution 2

Object.assign assigns all of the properties of the second argument to the first argument.

What the code does is if you pass an object into the constructor, it will assign those properties to the object that is being made. So for instance:

const todo = new Todo({ id: 1, title: 'hello' });
console.log(todo.title); // 'hello'

Edit: Because Object.assign is not type-safe, you should probably have the constructor accept something more specific than just an Object. I would suggest creating an interface for it.

Solution 3

Object.assign has no type checking. An alternative would be:

const assign = <T, K extends keyof T>(...args: T[]): T =>
    args.reduce( (result, current) =>
        (Object.keys(current) as K[]).reduce((target, key) => {
            target[key] = current[key];
            return target;
        }, result)
    , args[0])
;

Note that if T's properties aren't optional, every object passed in must include every property. If you can guarantee the presence of every property after the function returns, you can pass in the arguments as Partial<T>, then coerce the result when you're done.

Solution 4

Its just combining the two objects this and values. According to MDN

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.

Its used to create a shallow copy of the object and merge its properties with this which is the instance of Todo. In your given code this target object. Consider the below example

let target = {a:1,b:2};
Object.assign(target,{x:"new prop 1",y:"new prop 2"});
console.log(target) 
Share:
12,799

Related videos on Youtube

Nick Hodges
Author by

Nick Hodges

I'm a Delphi developer who is now really into Angular and I'm very interested in the Software Development process.

Updated on June 04, 2022

Comments

  • Nick Hodges
    Nick Hodges almost 2 years

    Somewhere along the way, I added a constructor to my Todo class:

    export class Todo {
      id: number;
      title: string;
      complete: boolean = false;
      editMode: boolean = false;
    
      constructor(values: Object = {}) {
        Object.assign(this, values);
      }
    }
    

    I don't understand the purpose of the code in the constructor.

    My application seems to work both with and without it, but I am hesitant to remove the code

    What is the purpose of Object.assign(...) in this constructor?

    • Jaromanda X
      Jaromanda X about 5 years
      Not sure why the answer was deleted, the only part of the answer that was incorrect was apparent "equivalent" code
  • Jaromanda X
    Jaromanda X about 5 years
    is equivalent to it may be equivalent, but you can't do that to this, and it's not really equivalent for other objects either
  • Jaromanda X
    Jaromanda X about 5 years
    as I said, you can't assign to this like that, so that won't work at all - but even when working with other objects, it's not the same result at all ...
  • Maheer Ali
    Maheer Ali about 5 years
    @JaromandaX I got the this part. But still can't get why both don't work same for other object. Would you mind giving an example?
  • zerkms
    zerkms about 5 years
    @MaheerAli you'd lose non-enumerable properties
  • Jaromanda X
    Jaromanda X about 5 years
    there's also this difference ... (look in the console)
  • Jaromanda X
    Jaromanda X about 5 years
    I think zerkms point is probably more important @MaheerAli - but you can see there's a difference (you said equivalent originally, and that's OK, but if someone doesn't know what Object.assign does, then the {...x} notation is probably going to confuse them even more :p
  • Superole
    Superole over 2 years
    One could argue that since Object.keys has no type checking either, you are really no closer to type nirvana. You have just added some code to hide the casting better, and added restrictions that may not fit the problems that Object.assign is often employed to solve.