TypeScript
TypeScript copied to clipboard
Combining destructuring with parameter properties
Today, we can take advantage of parameter properties to reduce the boilerplate, e.g:
class Person {
constructor(public firstName: string, public lastName: number, public age: number) {
}
}
Since 1.5, we can also use destructuring, e.g:
class Person {
firstName: string;
lastName: string;
age: number;
constructor({ firstName, lastName, age } : { firstName: string, lastName: string, age: number }) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
}
I've tried in many ways to combine both features, but had no success. So:
- Is it possible to combine them nowadays, and if yes, how?
- If not, could it be an improvement to a future TypeScript version? E.g:
class Person {
constructor(public { firstName, lastName, age } : { firstName: string, lastName: string, age: number }) {
}
}
// the code above would possibly transpile to:
var Person = (function () {
function Person(_a) {
var firstName = _a.firstName, lastName = _a.lastName, age = _a.age;
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
return Person;
})();
I like this.
@buzinas I originally had this working in #1671, but didn't take it in (check out #1541). I think the issue was that it was relatively dense in semantics. I don't necessarily agree, but maybe someone else on the team can weigh in.
Note that in those proposals the parameter property modifier applied to the entire binding pattern (simpler) as opposed to the original suggestion here which in theory supports different visibility modifiers per destructured element (not sure that level of specificity would be worth it).
@danquirk For me, if it's easier for you to do the other way, I don't really care. In fact, that was my first try (e.g public {firstName, lastName, age}), and as soon as it didn't work, I tried to use on each property, and it didn't work too.
It would be great if we could support both (since not always we want to create properties for all the parameters, and when we want, it would be simpler to use public/private only once), but if it's easier to support only one approach, it will be great already.
Probably it's something that people will use more and more, since destructuring is awesome.
just ran into this. I think either approach would satisfy most use cases. Hope to see this in a future version.
The first thing I tried was:
constructor(thisProps: { public myProperty: string, public myOtherProperty: number }) {
// etc
}
Something like this would be a nice-to-have.
Definitely +1 this. The alternative tends to be... bulky.
:+1:
:+1:
+1
+1
+1
👍
+1
Please use the GitHub reactions feature rather than standalone upvote comments. Thanks!
In an attempt to be more DRY using named args and strong types (until something like your proposal lands), I tried this:
interface ExampleArgs {
firstArg: string;
otherArg: number;
}
export default class Example implements ExampleArgs {
firstArg;
otherArg;
constructor(kwargs:ExampleArgs) {
return Object.assign(this, kwargs);
}
}
but got Member 'firstArg' implicitly has an 'any' type. errors for every argument. ☹️
Write this instead
interface ExampleArgs {
firstArg: string;
otherArg: number;
}
export default class Example {
constructor(kwargs:ExampleArgs) {
return Object.assign(this, kwargs);
}
}
export interface Example extends ExampleArgs { }
Thanks. I had to separate the exports from the declarations to make that work:
interface ExampleArgs {
firstArg: string;
otherArg: number;
}
class Example {
constructor(kwargs:ExampleArgs) {
return Object.assign(this, kwargs);
}
}
export default Example;
interface Example extends ExampleArgs { }
This would be incredibly useful for hydrating class-based models from JSON.
As in
export interface PersonDto {
name?: string;
}
export class Person {
constructor(public {name}: PersonDto = {}) {
}
}
Meanwhile we get this feature, here's the workaround:
export class PersonSchema {
firstName: string;
lastName: string;
email?: string; // Thanks to TypeScript 2, properties can be optional ;)
}
export class Person extends PersonSchema {
constructor(args: PersonSchema = {}) {
Object.assign(this, args);
}
}
The side effect is that if args has extra fields they will be copied into your new object.
This will also be your copy constructor.
Accepting PRs to implement constructor(public {name1, name2}) forms (i.e. no public / private etc inside the {}s).
@RyanCavanaugh, you're proposing that
constructor(public { name1 = value1, name2 = value2 }: { name1: string, name2: string }) {}
would desugar to
constructor({ name1 = value1, name2 = value2 }) {
this.name1 = name1;
this.name2 = name2;
}
right?
Why is this discussion only about constructors? Wouldn't this apply to other methods as well?!?
It can't apply to other methods as only the constructor can declare properties. On Fri, 2 Dec 2016 at 08:54, Dirk Möbius [email protected] wrote:
Why is this discussion only about constructors? Wouldn't this apply to other methods as well?!?
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/Microsoft/TypeScript/issues/5326#issuecomment-264394882, or mute the thread https://github.com/notifications/unsubscribe-auth/ACjP4sBomOO9wSXlIcw9NiDGf9-gZ2smks5rD86jgaJpZM4GRcCk .
+1
So, will this ever be implemented?
class Person {
firstName: string
lastName: string
age: number
constructor(_: Person) {
Object.assign(this, _)
}
}
class PersonWithEmail extends Person {
email: string
constructor(_: PersonWithEmail) {
super(_)
}
}
let p = new PersonWithEmail({
firstName: '',
lastName: '',
age: 0,
email: '',
})
@andraaspar I'd imagine you'd run into issues if there were methods on Person.
const model = {
firstName: 'Andras',
}
class Person {
firstName: string;
doAThing() {
}
}
const thisShouldError: Person = model;
@appsforartists Sure you would. Not a replacement for the real thing. But it's elegant for model objects, until it gets implemented.
Hi, personally I'm using a Schema interface to avoid issues with methods etc... there's an example here: https://blog.wishtack.com/2017/05/06/angular-2-components-communication-using-reactive-stores/
The problem with Object.assign is that you might end up with dynamically added properties.
Example:
let data = {firstName: 'Foo', superfluousProperty: 'something'};
let person = new Person(data);
console.log(person['superfluousProperty']) // 'something'
It would be nice to be able to use keyof to generate a runtime array of class properties but this is not available for the moment. There are also some experimental approaches like this one https://github.com/kimamula/ts-transformer-keys.
Anyway, I'm sure, we'll get something nice soon :)