“Don’t be afraid, don’t settle, work hard in the future” — Hello! I am little sesame 😄

Interfaces can be represented in object-oriented programming either as an abstraction of behavior or to describe the shape of an object

A, interfaces,

1. Use of interfaces

  • You can separate everything in interface with a semicolon or a comma, or you can do nothing at all, right
interface Person {
    name: string;
    age: number;
}

let tom: Person = {
    name: 'Tom'.age: 25
};
Copy the code

1.1 Object Shape

In the example above, we define an interface Person, followed by a variable Tom, whose type is Person. Thus, we are constrained that the shape of Tom must be the same as the interface Person.

  • It is not allowed to define a variable with fewer attributes than the interface:
interface Person {
    name: string;
    age: number;
}

let tom: Person = { // ERROR
    name: 'Tom'
};
Copy the code

  • Additional attributes are also disallowed:
interface Person {
    name: string;
    age: number;
}

let tom: Person = {
    name: 'Tom'.age: 25.gender: 'male' // ERROR
};
Copy the code

As you can see, when assigning a value, the shape of the variable must match the shape of the interface.

1.2 Behavior abstraction

Implements is an important concept in object orientation.

In general, a class can only inherit from another class. In some cases, different classes can have features in common. In this case, a feature can be extracted as an interface, which is implemented using the implements keyword. This feature greatly improves object-oriented flexibility.

  • An interface is an abstraction of properties and methods common to some classes that can be used to constrain the classes that implement the interface
  • A class can inherit from another class and implement multiple interfaces
  • Interfaces, like plug-ins, are used to enhance classes, whereas abstract classes are abstractions of concrete classes
  • A class can implement multiple interfaces, and an interface can be implemented by multiple classes, but a class can have multiple subclasses, but only one parent class

For example, a door is a class, and an anti-theft door is a subclass of a door.

If the security door has an alarm function, we can simply add an alarm method to the security door.

At this time, if there is another class, car, also has the function of alarm, you can consider the alarm extraction, as an interface, security door and car to achieve it:

interface Alarm {
    alert(): void;
}

class door {}class SecurityDoor extends door implements Alarm {
    alert() {
        console.log('SecurityDoor alert'); }}class Car implements Alarm {
    alert() {
        console.log('Car alert'); }}Copy the code

A class can implement multiple interfaces: separated by commas

interface Alarm {
    alert(): void;
}

interface Light {
    lightOn(): void;
    lightOff(): void;
}

class Car implements Alarm.Light {
    alert() {
        console.log('Car alert');
    }
    lightOn() {
        console.log('Car light on');
    }
    lightOff() {
        console.log('Car light off'); }}Copy the code

In the above example, the Car implements the Alarm and Light interfaces, which can both Alarm and turn on and off the headlights.

2. Interface properties

2.1 Optional Properties

An interface with optional attributes is similar to a normal interface definition, except that the optional attribute name definition is followed by one. Symbols.

interface Person {
    name: string; age? :number;
}
// The optional attribute means that the attribute may not exist.
let tom: Person = {
    name: 'Tom'
};

let tom: Person = {
    name: 'Tom'.age: 25
};
// It is still not allowed to add undefined attributes ** :
let tom: Person = {
    name: 'Tom'.age: 25.gender: 'male' // ERROR
};
Copy the code

Benefits of optional attributes:

  • One is that possible attributes can be predefined
  • Second, you can catch errors when references to nonexistent attributes.

2.2 Arbitrary Attributes

[propName: string]: any can be used when the new properties are not known in advance, and the propName name is arbitrary

  • Once any attribute is defined, the type of both the determined attribute and the optional attribute must be a subset of its type
interface Person {
    name: string; age? :number; // ERROR Once an arbitrary attribute is defined, the type of both the determinate attribute and the optional attribute must be a subset of its type
    [propName: string] :string;
}

let tom: Person = {
    name: 'Tom'.age: 25.gender: 'male'
};
Copy the code

In this example, the value of any attribute can be string, but the value of the optional attribute age is number. Number is not a child of string, so the error is reported.

  • Only one arbitrary attribute can be defined in an interface. If the interface has more than one type of attribute, you can use the union type in any attribute:
interface Person {
    name: string; age? :number;
    [propName: string] :string | number;
}

let tom: Person = {
    name: 'Tom'.age: 25.gender: 'male'
};
Copy the code

2.3 Read-only Properties

Some object properties can only change their value when the object is created. You can specify a read-only attribute with readonly before the attribute name:

interface Person {
    readonly id: number;
    name: string; age? :number;
    [propName: string] :any;
}

let tom: Person = {
    id: 89757.name: 'Tom'.gender: 'male'
};

tom.id = 9527; // ERROR 
Copy the code

In the above example, usereadonlyDefined propertiesidAfter initialization, it was assigned again, so it reported an error.

  • Note that the read-only constraint exists the first time an object is assigned, not the first time a read-only property is assigned:
interface Person {
    readonly id: number;
    name: string; age? :number;
    [propName: string] :any;
}

let tom: Person = { // ERROR 
    name: 'Tom'.gender: 'male'
};

tom.id = 89757; // ERROR
Copy the code

In the example above, there are two error messages. The first one is that the assignment to Tom is not given to id.

The second error occurs when assigning tom.id because it is read-only.

readonly vs const

The easiest way to determine whether to use readonly or const is to use it as a variable or as a property. Const as a variable, readonly as an attribute.

  • readonlyModified variables can only be modified inThe constructorIn the initialization
  • inTS,const 是constantIdentifier whose value cannot be reassigned
  • TSThe type system also allowsinterface,type,classThe attribute is identified asreadonly
  • readonlyIn fact, it’s justcompilePhase for code review. whileconstWill be inThe runtimeCheck (in supportconstThe grammaticalJSIn the runtime environment)

3. Interface inheritance

3.1 Interface Inheritance Interface

Like classes, interfaces can inherit from each other. This allows us to copy members from one interface to another, giving us more flexibility to split the interface into reusable modules.

interface Alarm {
    alert(): void;
}

interface LightableAlarm extends Alarm {
    lightOn(): void;
    lightOff(): void;
}
Copy the code

This is easy to understand, LightableAlarm inherits Alarm, in addition to the alert method, also has two new methods lightOn and lightOff.

  • An interface can inherit multiple interfaces to create a composite interface of multiple interfaces.
interface Alarm {
    alert(): void;
}
interface Wheel {
    go(): void;
}
interface LightableAlarmWheel extends Alarm, Wheel {
    lightOn(): void;
    lightOff(): void;
}
Copy the code

3.2 Interface Inheritance Classes

When an interface inherits a class type, it inherits the members of the class but not its implementation.

  • It is as if an interface declares all the members of a class, but does not provide an implementation.
  • Interfaces also inherit from classesprivate å’Œ protectedMembers. This means that when an interface is created that inherits a class with private or protected members, the interface type can only be implemented by that class or its subclasses (Implement).
class Control {
  private state: any;
}
interface SelectableControl extends Control {
  select(): void;
}
class Button extends Control implements SelectableControl {
  select(){}}class TextBox extends Control {
  select(){}}// Error: The "Image" type is missing the "state" attribute.
class Image implements SelectableControl {
  select(){}}class Location {}Copy the code

In the example above,

  • The SelectableControl interface contains all members of the Control class, including the private member State. Since state is a private member, only subclasses of Control can implement the SelectableControl interface. Because only subclasses of Control can have a private member state declared on the Control class.

  • Within the Control class, private member states are allowed to be accessed through instances of the SelectableControl interface. In fact, the SelectableControl interface is the same as the Control class with the Select method. The Button and TextBox classes are subclasses of the SelectableControl interface (because they both inherit from the Control class and have select methods), but the Image and Location classes are not.

whyTypeScriptWill interface inheritance classes be supported?[2]

Take the following as an example:

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y; }}interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1.y: 2.z: 3};
Copy the code

In fact, when we declare class Point, in addition to creating a class named Point, we create a type named Point (the type of the instance).

So we can either use Point as a class (use new Point to create an instance of it) :

Point can also be used as a type (use: Point to indicate the type of the argument) :

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y; }}const p = new Point(1.2); // Use it as a class

function printPoint(p: Point) { // Use it as a type
    console.log(p.x, p.y);
}

printPoint(new Point(1.2));
Copy the code

This example can actually be equivalent to:

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y; }}interface PointInstanceType {
    x: number;
    y: number;
}

const p = new Point(1.2);

function printPoint(p: PointInstanceType) {
    console.log(p.x, p.y);
}

printPoint(new Point(1.2));
Copy the code

The PointInstanceType we declared in the above example is equivalent to the Point type we created when we declared class Point.

So going back to the Point3d example, it’s easy to see why TypeScript supports interface inheritance classes:

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y; }}interface PointInstanceType {
    x: number;
    y: number;
}

// Equivalent to Interface Point3d extends PointInstanceType
interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1.y: 2.z: 3};
Copy the code

When we declare Interface Point3d extends Point, Point3d actually inherits the type of an instance of the class Point.

In other words, one interface, Point3d, is defined to inherit from another interface, PointInstanceType.

So there is no essential difference between “interface inheriting class” and “interface inheriting interface”.

Note that PointInstanceType lacks the constructor method compared to Point because the Point type created when the Point class is declared does not contain a constructor. Also, in addition to constructors, static properties or methods are not included (the type of the instance should certainly not include constructors, static properties, or static methods).

In other words, the Point type created when the Point class is declared contains only its instance attributes and instance methods:

class Point {
    /** static attribute, coordinate system origin */
    static origin = new Point(0.0);
    /** Static method, calculate the distance from the origin */
    static distanceToOrigin(p: Point) {
        return Math.sqrt(p.x * p.x + p.y * p.y);
    }
    /** Instance attribute, X-axis value */
    x: number;
    /** Instance attribute, y value */
    y: number;
    /** constructor */
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
    /** instance method, print this point */
    printPoint() {
        console.log(this.x, this.y); }}interface PointInstanceType {
    x: number;
    y: number;
    printPoint(): void;
}

let p1: Point;
let p2: PointInstanceType;
Copy the code

The last type Point in the above example is equivalent to the type PointInstanceType.

Similarly, when an interface inherits a class, it inherits only instance properties and instance methods.

4. Function type interface

In addition to describing ordinary objects with attributes, interfaces can also describe function types.

interface SearchFunc {
  (source: string.subString: string) :boolean;
}
Copy the code

With this definition, we can use this function type interface just like any other interface.

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
  let result = source.search(subString);
  return result > -1;
}
Copy the code

For type checking of function types, function parameter names do not need to match the names defined in the interface.

Indexable type interface

Used to constrain arrays and objects

Much like using interfaces to describe function types, we can also describe types that can be “indexed.” Indexable types have an index signature that describes the type of the object index and the corresponding index return value type.

interface StringArray {
  [index: number] :string;
}

let myArray: StringArray;
myArray = ["Bob"."Fred"]; 
// myArray = {0: "Bob", 1: "Fred"};

let myStr: string = myArray[0];
Copy the code

Type aliases

A type alias is used to give a type a new name.

type Name = string;
type NameResolver = () = > string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver) :Name {
    if (typeof n === 'string') {
        return n;
    } else {
        returnn(); }}Copy the code

Interface VS type alias

  • The interface creates a new name that can be called anywhere else. A type alias does not create a new name, for example if an error message is reported, the alias is not used
  • Type aliases cannot be extends or implements, so we should use interfaces instead of type aliases whenever possible
  • Type aliases are more appropriate when we need to use associative or tuple types

reference

[1]. TypeScript Chinese

[2]. Interface inheritance class