在TypeScript中,一个接口可以扩展一个类,对于什么?

时间:2016-08-24 13:38:04

标签: inheritance typescript interface

TypeScript Handbook中的

在'使用类作为接口'部分,有一个扩展类的接口示例。

class Point { ... } interface Point3d extends Point {...}

这什么时候有用?你有这方面的实际例子吗?

4 个答案:

答案 0 :(得分:8)

以此课程为例:

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
    }
}

您可以像这样创建一个实例:

let a1 = new MyClass(4, "hey");

但您也可以创建一个满足相同精确界面的对象,如下所示:

let a2 = {
    num: 3,
    str: "hey",
    fn: function(arr: any[]): boolean {
        // do something
    }
}

a1instanceof MyClass,而a2只是一个对象,但它们都实现了相同的界面。
扩展类的接口就是这样,你可以使用类定义和扩展它的接口。

由于语言的性质,可能只是一种可能性,但这里有一个可能有用的例子:

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) { ... }

答案 1 :(得分:6)

Interfaces section of TypeScript Handbook中所述:

  

接口甚至继承基类的私有成员和受保护成员。这意味着当您创建一个扩展具有私有或受保护成员的类的接口时,该接口类型只能由该类或其子类实现

这种限制似乎是私人和受保护成员继承的副作用。

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
    }
}

答案 2 :(得分:3)

我也很难理解“你为什么要这么做?”这就是我学到的东西。

如前所述,接口甚至继承基类的私有成员和受保护成员。这意味着,当您创建一个扩展带有私有或受保护成员的类的接口时,该接口类型只能由该类或其子类实现。

假设您正在为用户界面实现控件。您需要按钮,文本框和标签之类的标准控件。

层次结构为:

class Control{
    private state: any;
}
class Button extends Control{
}
class TextBox extends Control{
}
class Label extends Control{
}

请注意Control中的私有状态值。那很重要。

现在假设我们希望有一种方法可以引用可以通过某些激活触发的控件。例如,可以单击一个按钮。输入文本并按Enter键时,可以激活文本框。但是,标签是装饰性的,因此用户无法对其进行任何操作。

我们可能想要一种引用此类控件的方法,以便仅使用那些类型的控件就可以执行某些操作。例如,我们可能想要一个接受控件作为参数的函数,但我们只希望可以激活的控件。

假设我们尝试使用普通界面描述这些控件,而普通界面不会扩展类(这是不正确的,但我会稍后解释原因)。

// WARNING: This isn't correct - see rest of post for details.
interface ActivatableControl{
    activate(): void;
}

任何实现接口的方法都可以视为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{}

如上所述,Label不实现ActivatableControl。一切都很好,对吧?

问题出在这里-我可以添加另一个实现ActivatableControl的类:

class Dishwasher implements ActivatableControl{
    activate(){}
}

该界面的目的是用于可以激活的控件,而不是无关的对象。

所以我真正想要的是指定一个需要某些控件可以激活的接口,

为此,我将界面扩展为Control,如下所示:

class Control{
    private state: any;
}
interface ActivatableControl extends Control {
    activate(): void;
}

由于Control具有私有值,因此仅Control的子类可以实现ActivatableControl

现在,如果我尝试这样做:

// Error!
class Dishwasher implements ActivatableControl{
    activate(){}
}

我将收到Typescript错误,因为洗碗机不是控件。

附加说明:如果类扩展了Control并实现了activate,则可以将其视为ActivatableControl。本质上,即使未明确声明接口,该类也实现了该接口。

因此TextBox的以下实现仍使我们将其视为ActivatableControl:

class TextBox extends Control {
    activate(){}
}

这是层次结构的最终版本,其中一些代码显示了我可以使用的功能:

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();
}

答案 3 :(得分:2)

  • 限制界面的使用。
  • 如果我们不希望任何类可以实现接口,则可以使用此解决方案。
  • 假设接口“ I”扩展了类“ C”。那么“ I”只能由“ C”或其子代实现。
  • 或者如果一个类需要实现“ I”,那么它应该首先扩展“ C”。

请参见下面的示例。

// 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
    }
}
相关问题