2

It's been a while since the constructor shorthand was introduced, and it's really useful. Now I can write this:

class Dog {
  constructor(
    public age: number,
    public weight: number,
  ) {}
}

instead of this:

class Dog {
  public age: number
  public weight: number

  constructor(
    age: number,
    weight: number,
  ) {
    this.age = age
    this.weight = weight
  }
}

And it works even better for more complex classes.

I wonder if there is some shorthand for class constructor with named parameters (using object destructing)? I think new Dog({age: 3, weight: 8}) is much clearer than new Dog(3, 8). No chance of misplacing the argument positions. But class definition for it looks really ugly:

class Dog {
  // 1. declare properties
  public age: number
  public weight: number

  constructor({
    // 2. object destructing
    age,
    weight,
  }: {
    // 3. declare object type
    age: number,
    weight: number,
  }) {
    // 4. assign values
    this.age = age
    this.weight = weight
  }
}
// x4 duplicates, no DRY at all
1
  • 1
    You might be looking for AssignCtor from here, as shown in this playground link. It works because all you're doing is assigning the properties of the constructor argument to the instance. If that meets your needs I'll write an answer or close as a duplicate. If not, what am I missing? Commented Apr 4 at 2:23

4 Answers 4

2

No there is not and probably never will.

Property definition at the constructor level are called Parameter properties.

Those parameter properties are one of those deviation where typescript isn't just typing over JS but also generate some code.

class Dog {
  constructor(
    public age: number,
    public weight: number,
  ) {}
}

generates the following JS

class Dog {
    age;
    weight;
    constructor(age, weight) {
        this.age = age;
        this.weight = weight;
    }
}

What you're asking for new Dog({age: 3, weight: 8}) & new Dog(3, 8) are wildly differnt.

One is an object that has no meaning in JS and the other is what happens at runtime.

Beside a few exceptions TS isn't meant to generate code for you.

Sign up to request clarification or add additional context in comments.

Comments

1

There's no syntax for that but you could make some DRY by introducing an interface and merge it into the class:

Playground

interface DogProps {
    age: number,
    weight: number,
}
interface Dog extends DogProps {}
class Dog {
    constructor(props: DogProps){
        Object.assign(this, props);
    }
    feed(){
        console.log('Thank you, my Snack Lord');
    }
}

const dog = new Dog({age: 3, weight: 20});
dog.age // number

Comments

1

As we know, JS, may be therefore TS as well, does not support Named parameters.

Citations: Using named parameters in JavaScript (based on TypeScript)

Therefore object destructuring assignment pattern may be a useful workaround to name parameters. However, using object types for the purpose of naming parameters may have impact in using Parameter Properties.

The Parameter Properties have a special syntax prefixing constructor parameters with one of the visibility modifiers public, private, protected, or readonly. However, visibility modifiers are not allowed to use with object type members.

The following syntax will throw the error 'public' modifier cannot appear on a type member.

class Dog {
  constructor({
    age,
    weight,
  }: {
   public age: number,
   public weight: number,
  }) {
  }
}

A work around:

The following workaround uses a factory function in which parameter is typed with the respective object type. This lets the provision of named parameters. It then invokes the private constructor through new keyword. This constructor has been defined with Parameter properties.

 class Dog {

  static GetObject({
        age,
        weight,
      }: {
        age: number,
        weight: number,
      }){

    return new Dog(age,weight)

  }
     private constructor(
        public age : number,
        public weigth :  number,
) {}
}

const obj = Dog.GetObject({age:1, weight:2})

Comments

1

Other answers directly address your question and give you more involved workarounds, but I wanted to suggest some simpler ways to DRY up your code:

  1. Class field types can be inferred from constructor's implementation, so you can drop two type annotations (playground):

    class Dog {
      public age
      public weight
    
      constructor({
        age,
        weight,
      }: {
        age: number,
        weight: number,
      }) {
        this.age = age
        this.weight = weight
      }
    }
    
  2. Unless in your real code the constructor's implementation is long/complex (which is its own code smell), I don't think destructuring the parameter pulls its weight. Instead you can access age and weight as properties (playground):

    class Dog {
      public age
      public weight
    
      constructor(attributes: {
        age: number,
        weight: number,
      }) {
        this.age = attributes.age
        this.weight = attributes.weight
      }
    }
    

It's still a bit repetitive, but better than it was in your original playground.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.