Consider the following TypeScript classes:
class A {
foo: string;
constructor({ foo }: { foo: string }) {
this.foo = foo;
}
equals(other: A) {
return this.foo === other.foo;
}
}
class B extends A {
bar: number;
constructor(b: {
foo: string;
bar: number;
}) {
super({ foo: b.foo });
this.bar = b.bar;
}
}
const b = new B({
foo: 'Test',
bar: 42,
});
In reality, class B has about 20 properties which makes the class and especially the constructor definition super verbose because every property is repeated three separate times. How could I make it more compact?
You can’t do constructor({ foo, bar }: B)
because B also requires the object to have things like equals()
.
I also tried the following but same result:
constructor(b: { [K in keyof B]: B[K] }) {
super({ foo: b.foo });
.
.
.
Argument of type '{ foo: string; bar: number; }' is missing the following properties from type '{ [K in keyof B]: B[K] }': equals.ts(2345)
3
Answers
There’s a couple different approaches you can take.
Introspect the classes themselves
Given a couple helper types, you can do
You can of course also limit those types using
Pick
andOmit
if required.Using
ConstructorParameters
You can also use
ConstructorParameters
to introspect the superclass’s constructor’s parameters, and destructuring in the subclass’s constructor.This could be useful for more complex cases.
With
ConstructorParameters
, you could at least get toAs an alternative, if your not creating 1000’s of the same instance of a class, you might find not using classes would actually work better. I find Typescript much easier keeping things functional. Due to Javascript scope / closures etc, I find JS classes are rarely needed.
With that in mind, your above could be simplified to something like ->
TS Playground
Another advantage here of going functional, is that you get the massive added bonus of composition. IOW: your not bounded by Javascripts OOP single inheritance model. Of course the downside is that all typing is duck typing, eg. You couldn’t for instance use
instanceOf
etc. You would be required to do ->if ('foo' in ...
etc.To avoid writing dozens of unnecessary lines, you do it like this: