Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 925
Nick Hodges Avatar asked Apr 09 '19 01:04

Nick Hodges


2 Answers

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);
  }
}
like image 184
Ali Habibzadeh Avatar answered Oct 21 '22 19:10

Ali Habibzadeh


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.

like image 27
Benjamin Davies Avatar answered Oct 21 '22 21:10

Benjamin Davies