Reputation: 424
A standard class declaration looks like this:
class Foo {
a: string;
b: string;
constructor({ a, b }: { a: string, b: string }) {
this.a = a;
this.b = b;
}
}
Which requires you to copy a variable 4 times -- twice with a type! Might not seem like that big a deal, but it creates needless boilerplate for a very simple task, and is problematic with string literals, as it requires you to have hard-coded strings in multiple places.
The only simpler way i found was using an interface:
interface Foo {
a: string;
b: string;
}
const getFoo = ({ a, b }: Foo): Foo => ({ a, b });
But now there's need to export both the getter and the interface, which is a bit backwards.
Is there a simpler way I'm missing?
I'd love to see something like Dart's initializers:
class Foo {
String a;
String b;
Foo({ this.a, this.b });
}
Update:
Was inspired by one of the answers to take a more functional approach:
interface Test {
a: string;
b: string;
}
export function get<T>(params:T):T {
return params;
}
get<Test>({ a: 'hello', b: 'world' });
Which is unfortunately missing default values.
Upvotes: 1
Views: 394
Reputation: 2769
You can avoid a lot of var assignment code by using parameter properties. For example, assuming you aren't required to pass an object to the constructor, you can declare the same class as follows:
class Foo {
constructor(readonly a: string, readonly b: string) {
}
}
Upvotes: 1
Reputation: 329608
One way to avoid this sort of boilerplate is to refactor it into your own library function (which might need to use a type assertion to convince the compiler not to complain)
function ifaceToCtor<T extends object>(): new (param: T) => T {
return class {
constructor(arg: any) {
Object.assign(this, arg);
}
} as any;
}
Then instead of making a normal class, you define an interface and make its constructor with ifaceToCtor
:
interface Foo { a: string, b: string };
const Foo = ifaceToCtor<Foo>(); // short!
And you can verify it works:
const foo = new Foo({ a: "eh", b: "bee" });
console.log(foo.a); // eh
console.log(foo.b); // bee
This is similar to your "getter" idea, but this one is a real class (if it matters) and uses Object.assign()
to avoid the duplication of property names and allowing it to be tucked away into some library you don't need to look at.
Similarly you can use ifaceToCtor
to extend a nonce superclass to add methods or other props not part of your constructor parameter:
class Bar extends ifaceToCtor<{ c: string, d: number }>() {
method() {
return this.c.toUpperCase() + " " + this.d.toFixed(2);
}
}
const bar = new Bar({ c: "hello", d: 123.456 });
console.log(bar.method()); // HELLO 123.46
And this version has the advantage that what you are exporting is a standard-looking class
, where the interface and constructor named Bar
are brought into scope for you without requiring the interface
-and-const
from the first example.
Okay, hope that helps; good luck!
Upvotes: 2