admin管理员组文章数量:1122846
In my understanding, this is how you define interface inside class:
class A {
a: number
constructor() {
this.a = 0
}
}
And this is how you define interface outside class:
interface A {
a: number
}
class A {
constructor() {
this.a = 0
}
}
What is the difference between the two cases? When should I use one over the other? I notice that private
and protected
modifiers can't be used in interface, so if I need to use them then of course I need to use the inside case. But if I only use public
, then it seems to be no difference between them?
Reading the documentation for Classes and Everyday Types doesn't help.
In my understanding, this is how you define interface inside class:
class A {
a: number
constructor() {
this.a = 0
}
}
And this is how you define interface outside class:
interface A {
a: number
}
class A {
constructor() {
this.a = 0
}
}
What is the difference between the two cases? When should I use one over the other? I notice that private
and protected
modifiers can't be used in interface, so if I need to use them then of course I need to use the inside case. But if I only use public
, then it seems to be no difference between them?
Reading the documentation for Classes and Everyday Types doesn't help.
Share Improve this question edited Nov 24, 2024 at 13:32 Ooker asked Nov 22, 2024 at 14:44 OokerOoker 2,9105 gold badges38 silver badges79 bronze badges 10 | Show 5 more comments1 Answer
Reset to default 2What you're calling "defining an interface inside a class" is just the normal way you write classes. Fields generally are declared in the class:
class A {
a: number
constructor() {
this.a = 0
}
}
If you compile the above to target modern JavaScript and use the appropriate compiler options, it will produce a JavaScript class with a public class field:
// JavaScript
class A {
a; // <-- public class field declaration
constructor() {
this.a = 0;
}
}
On the other hand, what you're calling "defining an interface outside a class" is known as declaration merging. The interface merges with the class instance type, which sort of "patches" it at the type level. Interface declarations have absolutely no runtime effect, so anything you put in there that normally goes into the class declaration might behave differently. For example:
interface B {
a: number
}
class B {
constructor() {
this.a = 0
}
}
If you compile that with the same compiler settings as above, you will get the following JavaScript, without a class field declaration:
// JavaScript
class B {
// <-- no public class field declaration
constructor() {
this.a = 0;
}
}
The fact that the two approaches can result in different JavaScript is an indication that they are not identical, although there's little pragmatic difference in this example. Still, you could easily change things where there are noticeable differences:
class C { a?: number }
console.log(Object.keys(new C()).length) // 1
interface D { a?: number }
class D { }
console.log(Object.keys(new D()).length) // 0
And certainly there are type checking differences. Interface merging adds stuff without having to pass the normal sorts of type checks:
class E { a: number } // compiler error
// ~
// Property 'a' has no initializer and is not definitely
// assigned in the constructor.
new E().a.toFixed(); // runtime error
interface F { a: number }
class F { } // no compiler error
new F().a.toFixed(); // runtime error
In the above, E
gives you a compiler error that you forgot to initialize a
, whereas F
does not give you that error, even though the same problem exists. So E
warns you about an issue that F
doesn't notice.
Also, interfaces are not class declarations, so many class
-specific things you can normally do are not available in interface merging. The most obvious example is that classes allow you to write runtime code, whereas interfaces do not. You can initialize class fields and implement class methods, but "initializing" an interface property or "implementing" an interface method doesn't make much sense:
class G { a: number = 1; b(): number { return 2 } };
interface H { a: number = 1, b(): number { return 2 }} // errors!
Any class-specific modifiers like private
or protected
or static
or abstract
are unavailable in interfaces:
abstract class I {
abstract a: number;
private b?: number;
static c: number;
}
interface J {
abstract a: number; // error!
private b?: number; // error!
static c: number; // error!
}
As for what you should do, this is at least somewhat subjective. In my opinion, declaration merging is an advanced technique that you only use to work around limitations in existing code. If you can do something without using it, you should.
Playground link to code
本文标签: typescriptWhat is the difference between defining interface outside and inside a classStack Overflow
版权声明:本文标题:typescript - What is the difference between defining interface outside and inside a class? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736303064a1931756.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
interface
defines a public interface, allowing other modifiers would make no sense - stackoverflow.com/q/32888353/3001761. – jonrsharpe Commented Nov 22, 2024 at 14:51--useDefineForClassFields
then there will be noObject.defineProperty()
for a merged field; you can't assignreadonly
merged fields in the ctor, etc). You can certainly usereadonly
, butprivate
doesn't work on an interface in general. Does this fully address the q? If so I'll write an a. If not, what's missing? – jcalz Commented Nov 22, 2024 at 19:05readonly
and optional modifiers. If you want to know whyprotected
orprivate
aren’t supported in interface declarations that’s a fair question, I suppose, but can you please edit to clarify and distinguish between the modifiers because they are not the same thing. – jcalz Commented Nov 23, 2024 at 4:25