Home > Software engineering >  How to use javascript object destructuring on a subset of an argument and maintain reference to all
How to use javascript object destructuring on a subset of an argument and maintain reference to all

Time:02-04

I'm trying to figure out if there's a way to use typescript/javascript object destructuring to target only a subset of the objects properties while maintaining the remaining properties of the object in its own variable, "catching all other properties/keys".

My use case is related to using inheritance to extending a class while attempting to leave the api's the similar. In the example below I only want to use object destruction for properties onlyForB and alsoOnlyForB while pass all remaining properties to config variable.

class A {
   constructor(config) {
     this.name = config.name;
     // ...
   }
}

class B extends A {
  constructor({ onlyForB, alsoOnlyForB, ..config }) { // using ...config doesn't work
    this.onlyForB = onlyForB;
    this.alsoOnlyForB = alsoOnlyForB;
    super(config);
  }
}


const b = new B({ onlyForB: "B", alsoOnlyForB: "B2", name: "Satoshi", age: 100});
/**
 Trying to achieve the following in class B's constructor

   onlyForB -> "B"
   alsoOnlyForB -> "B2"
   config -> { name: "Satoshi", age: 100 }

*/

When I try this with { onlyForB, alsoOnlyForB, ..config } which is similar to creating objects with the spread syntax I get errors. In the real use case I am extending an open source class that has mainly "config" properties and using typescript.

Is there a way to achieve this without manually deleting all the object's properties specific to the B class?

Side Note: For those familiar with python I am trying to achieve something similar to what **kwargs does, ie. def __init__(self, onlyForB, alsoOnlyForB, **kwargs).

CodePudding user response:

There are lots of possible ways to do something like this; here is one example:

type ConfigForB = {
    onlyForB: string,
    alsoOnlyForB: string,
    age: number,
    name: string
}

type ConfigForA = Omit<ConfigForB, 'onlyForB' | 'alsoOnlyForB'>

class A {
   private name: string 
   constructor(config: ConfigForA) {
     this.name = config.name;
     // ...
   }
}

class B extends A {
  private onlyForB: string
  private alsoOnlyForB: string
  constructor({ onlyForB, alsoOnlyForB, ...config }: ConfigForB) {
    super(config);
    this.onlyForB = onlyForB;
    this.alsoOnlyForB = alsoOnlyForB;
  }
}

const b = new B({ onlyForB: "B", alsoOnlyForB: "B2", name: "Satoshi", age: 100});

I've defined a ConfigForB type that has the four properties listed in your example, and used that type in the constructor for B. Then I've used the Omit<Type, Keys> utility type to construct a new ConfigForA type, which is exactly the same as ConfigForB, except it's without the onlyForB and alsoOnlyForB keys.

If I were to define this explicitly, it would look like this:

type ConfigForA = {
    age: number,
    name: string
}

This would also work, by the way - using Omit is just nice since you don't have to repeat yourself.

You can try this out on TS Playground here.

  •  Tags:  
  • Related