4

I have a Foo and a Bar class:

class Foo {
  
  private name: string;
  private bar: Bar;

  constructor(name: string, bar: Bar) {
    this.name = name;
    this.bar = bar;
  }
}

class Bar {
  private x: number;
  private y: number;

  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
}

Now using the Typescript constructor shorthand I could write Foo as:

class Foo {
  constructor(private name: string, private bar: Bar) { }
}

However what I would like to do, is instead of passing a Bar object into the Foo constructor. Is instead pass in the values of x and y, but still maintain using the shorthand notation to save me writing out the class in full.

Effectively, can this:

class Foo {
  
  private name: string;
  private bar: Bar;

  constructor(name: string, x: number, y: number) {
    this.name = name;
    this.bar = new Bar(x, y);
  }
}

Be written making use of the shorthand notation?

2
  • Could you help us understand why you want to pass the x and y values as separate parameters instead of a Bar object? Is it to encapsulate the Bar creation inside the Foo constructor, rather than creating one in calling code? Commented Sep 25, 2020 at 13:29
  • Yes that's the idea, encapsulate the creation to 'Foo' so that any other classes instantiating a 'Foo' don't need to know about 'Bar' Commented Sep 25, 2020 at 13:47

1 Answer 1

4

The shorthand notation is called "parameter properties", and specifically just copies a constructor parameter to a same-named class property. It isn't suitable for what you're doing with bar. You can still use the shorthand for the name property, like this:

class Foo {
    private bar: Bar;
    constructor(private name: string, x: number, y: number) {
        this.bar = new Bar(x, y);
    }
}

which is how I'd recommend you proceed.


If someone were to threaten me with bodily harm unless I came up with some way to use parameter properties for your bar scenario, I suppose I could abuse default parameters like this:

// ☠ DON'T DO THIS ☠
class AbuseFoo {
    constructor(
        private name: string,
        x: number, y: number,
        private bar: Bar = new Bar(x, y)
    ) { }
}

This "works" in that

// ☠ DON'T DO THIS ☠
let f = new AbuseFoo("name", 1, 2);
console.log(JSON.stringify(f)); // {"name":"name","bar":{"x":1,"y":2}}

produces a value containing a private bar property whose x is 1 and y is 2. But it exposes an optional fourth constructor parameter that overrides the x and y properties:

f = new AbuseFoo("name", 1, 2, new Bar(3, 4)); 
console.log(JSON.stringify(f)); // {"name":"name","bar":{"x":3,"y":4}}

And of course, it's ugly and more complicated than just declaring bar in the class and setting it in the constructor the normal way. So unless you have some very good reason to do this (e.g., code obfuscation contest, threats of bodily harm, etc), I'd stick with the original answer:

No, you can't use parameter properties to set bar that way. Use the longhand method.

Playground link to code

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

5 Comments

While I'm assuming the OP is completely sensible and will listen to your suggestion to use the longhand method - it upsets me to think that some future visitors won't be as sensible, skip reading the important bits, and just copy-paste and abuse default parameters :(
How about class Foo { constructor(public name: string, private bar: Bar) { this.bar = new Bar(); }} ? I know, it's still weird but at least the caller can't override it typescriptlang.org/play?#code/…
@JuanMendes So you still have to pass in a Bar with new Foo() and then it gets overwritten? I don't think that's what is being asked for here, but I could be wrong.
Your recommended way is perfect! I didn't realise you could have some as parameter properties but not others. Thanks very much
@GeoffJames Hmm, interesting point. I've edited to try to be more explicit that copy-pasters will be getting something bad. If I thought such things would lead to loss of life or limb I'd remove it, but as it stands I think it's fun and instructive to hypothetical future visitors to see why the default parameter workaround is bad, since for all I know they would come up with the idea themselves.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.