0

In TypeScript 3.6.3 it seems I cannot use destructured assignment to initialize Class properties in a constructor, even if the passed object conforms to an appropriate Interface. Is there a way to achieve this, or is there good rationale for why it's not allowed?

interface TestInt {
    name: string,
    name2: string
}

class TestClass {
    name: string
    name2: string
  constructor(t: TestInt){   
    {this.name, this.name2} = t
  }
}

// static errors: 
// Property 'name' has no initializer and is not definitely assigned in the constructor.
// Property 'name' is used before being assigned.

let a = new TestClass({name: 'mom', name2:'dad'})
console.log(a.name)
console.log(a.name2)

// undefined
// undefined
Josh Diehl
  • 2,913
  • 2
  • 31
  • 43

1 Answers1

2

Destructuring works fine; the statement {name, name2} = t defines or assigns name as t.name and name2 as t.name2. However, because you're referring to this.name and this.name2, Typescript does not remove the this. and cannot perform the assignment.

You can see this work with arrays:

constructor(t: TestInt){   
  [this.name, this.name2] = [t.name, t.name2];
}

typescript playground

And you can see it work with the verbose {name: this.name} syntax as long as you parenthesize the assignment. Otherwise Typescript treats the braces as a block and name as a label.

constructor(t: TestInt){   
  ({name: this.name, name2: this.name2} = t);
}

typescript playground

Though you can use Object.assign to assign t's properties to this, Typescript does not correctly infer that all properties of TestClass are definitely assigned even if we assume all of t's properties are present as defined. To be fair, this is not safe if t only contains name or name2 through its prototype chain.

class Obj {
  get name() { return 'foo'; }   // compare with name = 'foo', noting "name" collision
}

class Obj2 extends Obj {
  get name2() { return 'baz'; }  // compare with name2 = 'bar'
}

let testClass = new TestClass(new Obj2());
window.alert(`${testClass.name} ${testClass.name2}`);

typescript playground

Related:

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • So for destructured assignment to work, the fully qualified variable name has to exactly match the one on the right hand side? – Josh Diehl Sep 28 '19 at 02:12
  • 1
    @Josh Yes, if you want the syntax to work without using colons, you need the exact dict key to be your single valid target identifier. There was some discussion about that (and the need for colons when setting specific properties) on [this SO question](https://stackoverflow.com/q/29620686/1426891) that links to [this ES6 discussion thread from March 2015](https://mail.mozilla.org/pipermail/es-discuss/2015-March/042195.html). – Jeff Bowman Sep 28 '19 at 15:10