What is an interface

TS Chinese document description

One of TypeScript’s core tenets is type-checking of the structure a value has. It is sometimes called “duck type discrimination” or “structural typing”. In TypeScript, interfaces name these types and define contracts for your code or third-party code.

When I first looked at TS, I felt abstract, so I put the concept aside and went through the whole document. Then I came to Learning typescript-interfaces and saw the definition and examples in this article, and suddenly I had a new understanding.

The following is a partial translation of the original text

Simply put, an interface is a way of describing the shape of an object. In TypeScript, we are only concerned with checking if the properties within an object have the types that are declared and not if it specifically came from the same object instance.

Simply put, an interface is a description of the shape of an object.

In TypeScript, we only care if an object has properties of a type we define, not if they are the same instance.

interface NameValue {
    name: string
}

let thisObj = { name: 'Bryan' };
let thatObj = { name: 'Bryan' };

function printName(name: NameValue) {
    console.log(name);
}

printName(thisObj); // 'Bryan'
printName(thatObj); // 'Bryan'

console.log(thisObj === thatObj) // false
Copy the code

To emphasize how TypeScript only checks the shape of objects, we have thisObj and thatObj with the same property, name. Despite being two different objects, both objects pass type checking when printName() takes each of them as arguments.

To highlight how Typescript checks only the shape of an object, the example creates thisObj and thatObj objects with the same property, although they are different objects that pass type checking when the printName method is called.

At this point, it makes sense to look at the definition of a duck (if you walk like a duck and quack, you’re a duck).

Summary of tutorial definitions with TutorialSteacher:

  • An interface is a description of the shape of an object
  • An interface defines the syntax of the class to follow
  • TypescriptDoes not convert the interface toJavascriptInstead, the interface is used for type checking.
  • An interface can define a type or implement it in a class (where the implementation of the interface is reflected in the class type)

Interface attributes and types

  • Optional attribute
  • Read-only property
  • Additional property checking
  • Function types
  • Indexable type
  • Class types
    • Implementing an interface
    • The difference between the static and instance parts of a class
  • Inherited interface
  • Mixed type
  • Interface inheritance class

Optional attribute

Grammar:

The property name? : Type description; eg: color? : string;

Application scenarios and functions: Pre-define possible attributes, and get an error type prompt for attributes that are not listed

As follows:

interfaceSquareConfig { color? :string; width? :number;
}

function createSquare(config: SquareConfig) :{ color: string; area: number } {
  let newSquare = { color: "white".area: 100 };
  if (config.clor) {
    // ⚠️⚠️⚠️ Error: Property 'clor' does not exist on type 'SquareConfig'
    newSquare.color = config.clor;
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}

let mySquare = createSquare({ color: "black" });
Copy the code

Read-only property

Grammar:

Read-only object properties

Readonly property: type description; eg: readonly x: number;

Read-only array properties

ReadonlyArray<T>

eg: let arr:ReadonlyArray<number> = [1,2,3,4,6]

Application scenario: An attribute cannot be modified once created

interface Point {
    readonly x: number;
    readonly y: number;
}
let p1: Point = { x: 10.y: 20 };
p1.x = 5; / / ⚠ ️ ⚠ ️ ⚠ ️ Error! Cannot assign to 'x' because it is a constant or a read-only property.
Copy the code

Additional property checking

To allow an interface to accept attributes that do not exist, write:

  • Recommended: String index signature[propName:string]:any
interfaceSquareConfig { color? :string; width? :string;
	[propName:string] :any;
}
const square:SquareConfig = {
	color: 'red'.opacity: 0.5
} // OK
Copy the code

Typescript validates because the additional attribute is of type any.

  • Assign an object with additional attributes to an object that is not described with additional attributes
interfaceSquareConfig { color? :string;
    width: number;

}
let squareOptions= {  width: 100.color: 'red'.opacity: 0.5 };
let squareOptions2:SquareConfig = squareOptions
Copy the code

This is ok because detection by assignment is looser than the literal form of an object, as long as the properties in the object are of the correct type for the interface description. Other additional attributes are not validated.

Function types

Interfaces can describe Function types as well as Object/Array types

Function Declaration (which describes the parameter type and return type)

interface PrintNums = {
    (num1: number.num2: number) :void; // void means it does not return anything
}

let logNums: PrintNums;

logNums = (num1, num2) = > {
    console.log(num1,num2);
}
Copy the code

The logNums function is no longer required to specify a type (logNums = (num1:number, num2:number):void => {}). TypeScript’s type system infers argument types by using logNums: PrintNums finds the type description for printNums.

Indexable type

Indexable types are types that can be indexed, such as a[0], a[‘hi’], and can be used to describe properties of arrays and object members.

The indexable type structure is shown in the figure below, with an index signature that describes the type of the object index and the corresponding index return value type.

Describing array members

interface numberArray {
    [propname:number] :number
}
const nums:numberArray = [1.3.5]
Copy the code

Description object member

interface Obj {
    [propname: number] :number.name:string,}const nums: Obj = {
    0: 1.name: 'lisa'
}
Copy the code

TypeScript supports two index signatures: strings and numbers. The numeric return value must be a subclass of the string return value.

class Animal {
    name: string;
}
class Dog extends Animal {
   age:number
}

interface Ok {
    [x: string]: Dog;
    [x: number]: Animal; // Error : Numeric index type 'Animal' is not assignable to string index type 'Dog'.
}
Copy the code

Because javascript converts numeric indexes to strings when using indexes, Dog needs to be a subclass of Animal. The example above is just the opposite.

The correct way to rewrite this example is:

class Animal {
    name: string;
}
class Dog extends Animal {
   age:number
}

interface Ok {
    [x: string]: Animal; // String index signature
    [x: number]: Dog; // Digital index signature
}
const animal: Animal = new Animal()
animal.name='bird'
const dog: Dog = new Dog()
dog.age = 1;
dog.name = 'pop'; 

const animals: Ok = {
    'cat': animal,
    0: dog,
}
Copy the code

Note that if the index signature is specified, the return value of other index signatures of the same type in the interface must meet the return value of the index signature

interface AnimalDefinition {
    [index: string] :string;
    name: string; // Ok as return type is "string"
    legs: number; // Error
}
Copy the code

Class types

Implementing an interface

Like languages like Java and C#, interfaces in TypeScript can be implemented using Class. The classes that implement the interface need to conform strictly to the structure of the interface.

interface IEmployee {
    empCode: number;
    name: string;
    getSalary:(number) = >number;
}

class Employee implements IEmployee { 
    empCode: number;
    name: string;

    constructor(code: number, name: string) { 
                this.empCode = code;
                this.name = name;
    }

    getSalary(empCode:number) :number { 
        return 20000; }}let emp = new Employee(1."Steve");
Copy the code

In the example above, the Employee class implements the interface IEmployee through the keyword Implement. Implementation classes should strictly define properties and functions with the same name and data type.

Typescript will report an error if the implementation class does not strictly adhere to the type of the interface.

Of course the implementation class can define additional properties and methods, but at least the properties and methods in the interface must be defined.

The difference between the static and instance parts of a class

I think the description of this part in the document is rather vague. Why does the example in the official website report an error

interface ClockConstructor {
    new (hour: number.minute: number);
}
/ / Error:
// Class 'Clock' incorrectly implements interface 'ClockConstructor'.
// Type 'Clock' provides no match for the signature 'new (hour: number, minute: number): any'.
  
class Clock implements ClockConstructor {
    currentTime: Date;
    constructor(h: number, m: number){}}Copy the code

Difference between the static and instance sides of classes

The interface declares the method/members that the instances have, and not what the implementing class has.

That is, the interface declares only methods/attributes that the instance has, and constructor is not part of the instance part, so an error is reported following the documented example.

So two concepts need to be clarified

  • Interfaces only declare methods/properties that the instance has.
  • constructorExists in the static part of the class, so it is not checked.

How do I implement the constructor section check? This stackOverflow article also provides an answer, referring to the Array implementation

interface Array<T> {
    length: number;
    toString(): string;
    toLocaleString(): string; push(... items: T[]):number;
    pop(): T | undefined; . [n:number]: T;
}

interface ArrayConstructor {
    new(arrayLength? :number) :any[];
    new <T>(arrayLength: number): T[];
    new<T>(... items: T[]): T[]; (arrayLength? :number) :any[];
    <T>(arrayLength: number): T[]; <T>(... items: T[]): T[]; isArray(arg:any): arg is Array<any>;
    readonly prototype: Array<any>;
}
Copy the code

That is, the constructor part is defined separately from the example part, with Array being a description of the instance part and ArrayConstructor being a description of the constructor part.

Below is a breakdown of the official website example

interface ClockConstructor { // ClockConstructor description
    new (hour: number.minute: number): ClockInterface; 
}
interface ClockInterface  { // ClockInterface instance description
    tick();
}

// createClock creates a constructor check
function createClock(ctor: ClockConstructor, hour: number, minute: number) :ClockInterface {
    return new ctor(hour, minute); // Ctor is the incoming class, in this example DigitalClock and AnalogClock
  // DigitalClock and AnalogClock implement the ClockInterface, so createClock returns type ClockInterface
}

class DigitalClock implements ClockInterface {
    constructor(h: number, m: number){}tick() {
        console.log("beep beep"); }}class AnalogClock implements ClockInterface {
    constructor(h: number, m: number){}tick() {
        console.log("tick tock"); }}let digital = createClock(DigitalClock, 12.17);
let analog = createClock(AnalogClock, 7.32);
Copy the code

Since Constructor is triggered when a new class is created, the createClock function is created to implement type checking on Constructor via new ctor. Because the classes DigitalClock and AnalogClock are implementations of the ClockInterface, the createClock function returns type ClockInterface.

Inherited interface

Similar to class inheritance, interfaces can also inherit.

This allows you to replicate other interfaces and extend them.

An interface can also inherit multiple interfaces.

interface Shape { 
     stokeWidth: number;
}
interface PenColor { 
     color: string;
}
interface Circle extends Shape, PenColor {
    solid: boolean
}

const circle: Circle = {
    stokeWidth: 1.color: 'red'.solid: false
}
Copy the code

Mixed type

There may be scenarios where an object can be used as both a function and an object, with additional attributes. Such as:

function Vue (options) {
  if(process.env.NODE_ENV ! = ='production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')}this._init(options)
}
Vue.version = '__VERSION__'

export default
Copy the code

You can use the following methods to describe interfaces in this scenario

interface Counter {
    (start: number) :string;
    interval: number;
    reset(): void;
}

function getCounter() :Counter {
    let counter = <Counter>function (start: number) {};// Assertions are used here
    counter.interval = 123;
    counter.reset = function () {};return counter;
}

let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
Copy the code

The Counter interface mixes descriptions of functions, function properties, and assertions in the examples on the official website

let counter = <Counter>function (start: number) { };

If you remove an assertion, you get an error

The error message mentions that the attribute interval is missing, but with assertions, it loses the significance of checking the function. In the example, the function that defines the interface returns a string, but no error is reported after assertions.

Object.assign

(T: T, U: U); object. assign

(T: T, U: U
,>
,>

interface Counter {
    (start: number) :string;
    interval: number;
    reset(): void;
}
let c: Counter = Object.assign(
(start:number) = > { return start+' ' },
    {
        interval: 10.reset: (a) :void= > { }
    }
)
c(10);
c.reset();
c.interval = 5.0;
Copy the code

Assign

(T: T, U: U) returns T & U
,>

Interface inheritance class

// working

The resources

  • tutorialsteacher-typescript-interface
  • Learning TypeScript — Interfaces
  • difference-between-the-static-and-instance-sides-of-classes
  • Build a function object with properties in TypeScript