I have an interface like
interface I {
a : string;
b : number;
c : string;
}
And now I'm declaring a class which will have public members a, b, c as per the interface I.
So I declare
class C implements I {
public a : string;
public b : number;
public c : string;
// and then constructor, other fields, other methods
}
Is there a way for me to avoid duplication in declaring fields? I.e. I want to have those fields without actually declaring them, by the virtue of extending the interface. So all I'll be left to write something like :
class C implements I {
// perhaps a magic statement that asserts that C should inherit all members from I
// and then constructor, other fields, other methods
}
To add insult to injury, there's even more duplication in the constructor because I cannot just initialize the fields from an object literal satisfying interface I in a one liner
constructor(data : I) {
this.a = data.a;
this.b = data.b;
this.c = data.c;
}
I do not have control over definition of the interface, I only have control over definition of the class (this means I cannot for instance turn interface into an abstract class...)
YES, it's possible. It requires some non-intuitive code but it will properly make sure you don't add redundant code.
You need the following line outside the class definition:
interface I {
a : string;
b : number;
c : string;
}
interface C extends I {}; //<--- this line
class C implements I {
constructor(data : I) {
this.a = data.a;
this.b = data.b;
this.c = data.c;
}
}
Playground Link
Now the TypeScript compiler will consider that the the class C has declared the same fields as the interface I.
It works like this:
interface C declares an interface called C.extends I means it inherits the properties from the existing interface I.{} is just the body of this interface C - it's empty, as we don't add or change anything.On its own, it's not very impressive. However, there is already a class called C, so the interface with the same name refers to the same interface that the class has. In simple terms,
class A {
public foo: string;
constructor(data: string) {
this.foo = data;
}
}
compiles in JavaScript (target ES6+) to
class A {
constructor(data) {
this.foo = data;
}
}
the information removed pertains to the interface of the class that TypeScript will use at compile time to make sure you use the class correctly. For example:
foo: string) according to their type (e.g., not treating foo as a number).Declaring an interface with the same name of a class will instead merge the declaration with that of the class. This is why the class interface can be manipulated. And in this case, we use extends (which is valid for interfaces) to enrich the class definition (where only implements is allowed).
As a reference I got the technique from this comment in an issue thread for the TypeScript repository on GitHub. The title of the GitHub issue is "Class declarations should implicitly implement interfaces" and the comment is by RyanCavanaugh from 17th of February 2016:
Note that in the latest version of TypeScript, you can use class/interface merging to do this:
interface Foo { a: number; } interface Baz extends Foo { } class Baz { constructor() { console.log(this.a); // no error here } }hat tip @jeffreymorlan for reminding me of this
[Note: the latest released version at the time of the comment would have been 1.8 Beta]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With