Interface details in TypeScript

  • 2020-06-15 07:48:38
  • OfStack

In TypeScript, interfaces are used as constraints, and when translated into JavaScript, all interfaces are erased because there is no concept of interfaces in JavaScript.

Let's start with a simple example:


function printLabel(labelledObj: { label: string }) {
  console.log(labelledObj.label);
}

var myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

So in this method, the type of labelledObj is {label: string}, which may seem a bit complicated, but we see from the myObj declaration below that it declares an object with the size attribute (value 10) and the label attribute (value "Size 10 Object"). Therefore, the type of method parameter labelledObj is {label: string}, indicating that the parameter has an label attribute of type string.

But, if you write it this way, it still seems a little confusing. The interface (interface) is then used to define the parameter types of this method.


interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

var myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

Optional attribute
Sometimes we don't need attribute 1 to exist, we can use the optional attribute 1 property to define it.


interface SquareConfig {
  color?: string;
  width?: number;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
  var newSquare = { color: "white", area: 100 };
  if (config.color) {
    newSquare.color = config.color;
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}

var mySquare = createSquare({ color: "black" });

Then we pass in an object that implements an SquareConfig interface into the createSquare method.

Why define it when it's completely dispensable? Defining optional attributes has two advantages over not defining them at all. 1, if there is an attribute, can constrain the type, which is 10 points key; 2. If color is written as collor in the method body by mistake, the compilation will not pass.

Method type

In JavaScript, method function is a basic type. In object-oriented thinking, interface implementation is done by classes, and function as a type, can implement the interface? The answer is yes.

In TypeScript, we can use interfaces to constrain the signature of methods.


interface SearchFunc {
 (source: string, subString: string): boolean;
}

var mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
 var result = source.search(subString);
 if (result == -1) {
  return false;
 }
 else {
  return true;
 }
}

In the above code, we define an interface that constrains the signature of a method that takes two string arguments and returns a Boolean value. In section 2 we declare the implementation of this interface.

Note that the compiler only checks for the correct type (argument type, return value type), so we can change the name of the argument to something else.


var mySearch: SearchFunc;
mySearch = function(src: string, sub: string) {
 var result = src.search(sub);
 if (result == -1) {
  return false;
 }
 else {
  return true;
 }
}

This is also compilable.

An array type
Above, we defined method types in the interface. How do we define array types? Very simple.


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

var myArray: StringArray;
myArray = ["Bob", "Fred"];

So myArray is 1 array, and the indexer is of type number, and the element is string.

In the interface definition, the indexer name is usually index (it can be changed to something else, but index is usually kept). So to


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

var myArray: StringArray;
myArray = ["Bob", "Fred"];

ok, too.

Note that indexers can only be of type number or string.


interface Array{
  [index: number]: any;
}

interface Dictionary{
  [index: string]: any;
}

Both of the above paragraphs are compilable.

One last thing to note is that if the interface is already an array type, all other attributes defined in the interface must be of the element type of the array. Such as:


interface Dictionary {
 [index: string]: string;
 length: number;  // error, the type of 'length' is not a subtype of the indexer
}

It will not compile. You need to change length to string.

Use classes to implement interfaces
In general, we prefer to use a class to implement the required interface, rather than using the interface directly as shown above.


interface ClockInterface {
  currentTime: Date;
}

class Clock implements ClockInterface {
  currentTime: Date;
  constructor(h: number, m: number) { }
}

In TypeScript, this is declared using the class keyword, which is the same as EcmaScript 6.

In addition, we can use interfaces to constrain methods defined in a class.


interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

var myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

0

In TypeScript, we can define constructors for interfaces.


interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

var myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

1

Then the naive of us might say:


interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

var myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

2

This is not possible!! Because the constructor is static (static), the class can only implement the instance (instance) part of the interface.

Isn't the constructor defined in this interface useless? Since TypeScript provides this functionality, it certainly won't be useless. The declared method is more specific:


interface ClockStatic {
  new (hour: number, minute: number);
}

class Clock {
  currentTime: Date;
  constructor(h: number, m: number) { }
}

var cs: ClockStatic = Clock;
var newClock = new cs(7, 30);

Normally we write new Clock, and here we point the Clock class to the ClockStatic interface. Note that the type of the newClock variable is any.

Inherited interface
Like class 1, interfaces also implement inheritance, using the extends keyword.


interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

var myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

4

You can also inherit multiple interfaces.


interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

var myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

5

It is important to note that although inheritance is supported for multiple interfaces, it is not compilable if the attributes of the same name defined in the inherited interface are of different types.


interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

var myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

6

This code will not compile because the type of the test attribute cannot be determined.

Use both the types described above
If only one type can be used, then the interface is too weak. But fortunately, our interface is very powerful.


interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

var myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

7

This USES three types: methods (the interface itself is a method), properties, and methods (which define method members).

This is the end of this article, I hope you enjoy it.


Related articles: