Home > Mobile >  How to avoid repeating class properties when they are defined as standalone type and initialised in
How to avoid repeating class properties when they are defined as standalone type and initialised in

Time:07-20

Let's say you have a simple ES6 class and you'd like to extract constructor parameters into its own type to be DRY. E.g.

type FooParams = {
  a: string;
  b: number;
  c: boolean;
};

export class Foo {
  public a: string;
  public b: number;
  public c: boolean;

  constructor({ a, b, c }: FooParams) {
    this.a = a;
    this.b = b;
    this.c = c;
  }
}

How can I avoid repeating class properties since they as exactly the same as type?

CodePudding user response:

Keep your properties in an object:

export class Foo {
  
  params: FooParams;

  constructor(params: FooParams) {
    this.params = params;
  }
}

CodePudding user response:

Conceptually you just want the constructor implementation to look like

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

where you use the Object.assign() method to copy all the (own, enumerable) properties from params into the class instance being constructed.

Unfortunately the compiler cannot tell from this that you intend to have Foo to have the same properties as FooParams. One way around this is to use declaration merging to inform the compiler that Foo also extends FooParams:

interface Foo extends FooParams { }
class Foo {
    constructor(params: FooParams) {
        Object.assign(this, params);
    }
}

Now things will work as desired:

const foo = new Foo({ a: "hey", b: Math.PI, c: true });
console.log(foo.a.toUpperCase()) // HEY
console.log(foo.b.toFixed(2)) // 3.14

The above isn't particularly type safe; declaration merging is kind of like a type assertion in that the compiler won't "check" it. If you forget the Object.assign() line in the constructor, the compiler won't notice, and you'll have runtime errors. So be careful.

Playground link to code

  • Related