在 TypeScript 中,一个接口可以扩展一个类,有什么用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39124915/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
In TypeScript an interface can extend a class, what for?
提问by zuraff
in TypeScript Handbookin 'Using a class as an interface' section, there is an example of an interface which extends a class.
在 “使用类作为接口”部分的TypeScript 手册中,有一个扩展类的接口示例。
class Point { ... }
interface Point3d extends Point {...}
class Point { ... }
interface Point3d extends Point {...}
When can this be useful? Do you have any practical examples of this?
这什么时候有用?你有这方面的实际例子吗?
回答by Nitzan Tomer
Take this class for example:
以这个类为例:
class MyClass {
public num: number;
public str: string;
public constructor(num: number, str: string) {
this.num = num;
this.str = str;
}
public fn(arr: any[]): boolean {
// do something
}
}
You can create an instance like so:
您可以像这样创建一个实例:
let a1 = new MyClass(4, "hey");
But you can also create an object that satisfies the same exact interface like so:
但是您也可以创建一个满足相同接口的对象,如下所示:
let a2 = {
num: 3,
str: "hey",
fn: function(arr: any[]): boolean {
// do something
}
}
The a1
is an instanceof
MyClass
, while a2
is just an object, but they are both implementing the same interface.
The point of interfaces extending classes is exactly that, you can take the interface that the class defines and extend it.
的a1
是instanceof
MyClass
,虽然a2
仅仅是一个对象,但它们都实现了相同的接口。
接口扩展类的要点正是,您可以获取类定义的接口并扩展它。
Maybe it's just a possibility due to the nature of the language, but here's an example of where it might be useful:
也许由于语言的性质,这只是一种可能性,但这里有一个可能有用的例子:
class Map<T> {
private _items: { [key: string]: T };
set(key: string, value: T) { ... }
has(key: string): boolean { ... }
get(key: string): T { ... }
remove(key: string): T { ... }
}
interface NumberMap extends Map<number> {}
interface StringMap extends Map<string> {}
interface BooleanMap extends Map<boolean> {}
function stringsHandler(map: StringMap) { ... }
回答by antonpv
As described in Interfaces section of TypeScript Handbook:
Interfaces inherit even the private and protected membersof a base class. This means that when you create an interface that extends a class with private or protected members, that interface type can only be implemented by that class or a subclass of it.
接口甚至继承基类的私有成员和受保护成员。这意味着当您创建一个接口来扩展具有私有或受保护成员的类时,该接口类型只能由该类或其子类实现。
Such restriction seems to be side effect of private and protected members inheritance.
这种限制似乎是私有成员和受保护成员继承的副作用。
class Parent
{
private m_privateParent;
}
interface ISomething extends Parent
{
doSomething(): void;
}
class NoonesChild implements ISomething
{
/**
* You will get error here
* Class 'NoonesChild' incorrectly implements interface 'ISomething'.
* Property 'm_privateParent' is missing in type 'NoonesChild'
*/
doSomething()
{
//do something
}
}
class NoonesSecondChild implements ISomething
{
/**
* Nope, this won't help
* Class 'NoonesSecondChild' incorrectly implements interface 'ISomething'.
* Types have separate declarations of a private property 'm_privateParent'.
*/
private m_privateParent;
doSomething()
{
//do something
}
}
class ParentsChild extends Parent implements ISomething
{
/**
* This works fine
*/
doSomething()
{
//Do something
}
}
回答by Jose
- to restrict usage of interface.
- can use this solution if we do not want any class can implement an interface.
- Suppose interface 'I' extends class 'C'. Then 'I' can be implemented only by 'C' or its children.
- or if a class needs implement 'I', then it should extend 'C' first.
- 限制接口的使用。
- 如果我们不希望任何类可以实现接口,可以使用此解决方案。
- 假设接口“I”扩展了类“C”。那么“I”只能由“C”或其子节点实现。
- 或者如果一个类需要实现“I”,那么它应该首先扩展“C”。
See an example below.
请参阅下面的示例。
// This class is a set of premium features for cars.
class PremiumFeatureSet {
private cruiseControl: boolean;
}
// Only through this interface, cars can use premium features.
// This can be 'licensed' by car manufacturers to use premium features !!
interface IAccessPremiumFeatures extends PremiumFeatureSet {
enablePremiumFeatures(): void
}
// MyFirstCar cannot implement interface to access premium features.
// Because I had no money to extend MyFirstCar to have PremiumFeatureSet.
// Without feature, what's the use of a way to access them?
// So This won't work.
class MyFirstCar implements IAccessPremiumFeatures {
enablePremiumFeatures() {
}
}
// Later I bought a LuxuryCar with (extending) PremiumFeatureSet.
// So I can access features with implementing interface.
// Now MyLuxuryCar has premium features first. So it makes sense to have an interface to access them.
// i.e. To implement IAccessPremiumFeatures, we need have PremiumFeatureSet first.
class MyLuxuryCar extends PremiumFeatureSet implements IAccessPremiumFeatures {
enablePremiumFeatures() {
// code to enable features
}
}
回答by HeroSteve
I also struggled with understanding "Why would you do this?" Here's what I learned.
我也很难理解“你为什么要这样做?” 这是我学到的。
As mentioned before, interfaces inherit even the private and protected members of a base class. This means that when you create an interface that extends a class with private or protected members, that interface type can only be implemented by that class or a subclass of it.
如前所述,接口甚至继承基类的私有成员和受保护成员。这意味着当您创建一个接口来扩展具有私有或受保护成员的类时,该接口类型只能由该类或其子类实现。
Imagine you are implementing controls for a user interface. You want standard controls like button, textbox, and label.
想象一下,您正在为用户界面实现控件。您需要标准控件,例如按钮、文本框和标签。
The hierarchy would be:
层次结构将是:
class Control{
private state: any;
}
class Button extends Control{
}
class TextBox extends Control{
}
class Label extends Control{
}
Notice the private state value in Control. That will be important.
注意 Control 中的私有状态值。那会很重要。
Now suppose we want a way to refer to controls that can be triggered by some activation. For instance, a button can be clicked. A textbox can be activated when you enter text and hit enter. However, a label is decorative, so a user can't do anything with it.
现在假设我们想要一种方法来引用可以由某些激活触发的控件。例如,可以单击一个按钮。当您输入文本并按 Enter 键时,可以激活文本框。然而,标签是装饰性的,所以用户不能用它做任何事情。
We might want a way to refer to such controls so we can do something with only those types of controls. For instance, we may want a function that accepts a control as a parameter, but we only want controls that can be activated.
我们可能想要一种引用此类控件的方法,以便我们可以仅对这些类型的控件执行某些操作。例如,我们可能想要一个接受控件作为参数的函数,但我们只想要可以激活的控件。
Suppose we try to describe those controls with a plain interface that doesn'textend a class (this isn't correct, but I'll explain why in a bit).
假设我们尝试用一个不扩展类的普通接口来描述这些控件(这是不正确的,但我稍后会解释原因)。
// WARNING: This isn't correct - see rest of post for details.
interface ActivatableControl{
activate(): void;
}
Anything implementing the interface can be treated as an ActivatableControl, so let's update our hierarchy:
任何实现该接口的东西都可以被视为一个 ActivatableControl,所以让我们更新我们的层次结构:
class Control{
private state: any;
}
interface ActivatableControl{
activate(): void;
}
class Button extends Control implements ActivatableControl{
activate(){}
}
class TextBox extends Control implements ActivatableControl{
activate(){}
}
class Label extends Control{}
As described above, Label doesn't implement ActivatableControl. So all is good, right?
如上所述,Label 没有实现 ActivatableControl。所以一切都很好,对吧?
Here's the problem - I can add another class that implements ActivatableControl:
这是问题 - 我可以添加另一个实现 ActivatableControl 的类:
class Dishwasher implements ActivatableControl{
activate(){}
}
The purpose of the interface was for controls that can be activated, not for unrelated objects.
该界面的目的是用于可以激活的控件,而不是用于不相关的对象。
So what I really want is to specify an interface that requires certain controlsto be activatable, and nothing else.
所以我真正想要的是指定一个需要某些控件才能激活的界面,而不是其他任何东西。
In order to do that, I make my interface extend Control, like this:
为了做到这一点,我让我的界面扩展 Control,如下所示:
class Control{
private state: any;
}
interface ActivatableControl extends Control {
activate(): void;
}
Since Control has a private value, only subclasses of Control can implement ActivatableControl.
由于 Control 具有私有值,因此只有 Control 的子类可以实现 ActivatableControl。
Now, if I try to do this:
现在,如果我尝试这样做:
// Error!
class Dishwasher implements ActivatableControl{
activate(){}
}
I'll get a Typescript error because Dishwasher isn't a Control.
我会收到 Typescript 错误,因为 Dishwasher 不是 Control。
Additional note: If a class extends Control, and implements activate, then it can be treated as an ActivatableControl. Essentially, the class implements the interface even though the interface isn't explicitly declared.
附加说明:如果一个类扩展了 Control,并实现了 activate,那么它可以被视为一个 ActivatableControl。本质上,即使没有显式声明接口,该类也实现了接口。
So the following implementation of TextBox still lets us treat it as an ActivatableControl:
所以下面的 TextBox 实现仍然让我们把它当作一个 ActivatableControl:
class TextBox extends Control {
activate(){}
}
So here's the final version of the hierarchy, with some code that shows what I can do with it:
所以这是层次结构的最终版本,其中一些代码显示了我可以用它做什么:
class Control {
private state: any;
}
interface ActivatableControl extends Control {
activate(): void;
}
class Button extends Control implements ActivatableControl {
activate() { }
}
// Implicitly implements ActivatableControl since it matches the interface and extends Control.
class TextBox extends Control {
activate() { }
}
class Label extends Control {
}
// Error - cannot implement ActivatableControl because it isn't a Control
/*
class Dishwasher implements ActivatableControl {
activate() { }
}
*/
// Error - this won't work either.
// ActivatableControl extends Control, and therefore contains state as a private member.
// Only descendants of Control can implement ActivatableControl.
/*
class Microwave implements ActivatableControl {
private state: any;
activate() { }
}
*/
let button: Button = new Button();
let textBox: TextBox = new TextBox();
let label: Label = new Label();
let activatableControl: ActivatableControl = null;
// I can assign button to activatableControl.
activatableControl = button;
// Same with textBox since textBox fulfills the contract of an ActivatableControl.
activatableControl = textBox;
// Error - label does not implement ActivatableControl
// nor does it fulfill the contract.
//activatableControl = label;
function activator(activatableControl: ActivatableControl){
// I can assume activate can be called
// since ActivatableControl requires that activate is implemented.
activatableControl.activate();
}