What is TypeScript Interface

A TypeScript interface is used to define the structure of an object, specifying the properties and methods that an object should have. This helps ensure that objects conform to a specific structure or contract, which can help catch potential errors during development and improve code readability.

Interfaces can also be used to define the structure of function, array, and indexable types, among other use cases. In addition, they can be extended and implemented, supporting inheritance and polymorphism.

Declaring Interfaces

Declaring interfaces in TypeScript involves using the interface keyword, followed by the interface name and a set of properties or methods within curly braces {}. Properties and methods should be separated by semicolons (;).

Syntax

interface interface_name {

}

Example

interface NDark {
  firstName: string,
  lastName: string,
  timeTravel: () => string
}

let jonas: NDark = {
  firstName: "Jonas",
  lastName: "Kanwald",
  timeTravel: (): string => { return "Sic Mundus Creatus est" }
}

console.log(jonas.firstName)
console.log(jonas.lastName)
console.log(jonas.timeTravel())

The example defines an interface. The Jonas object is of the type NDark. Hence, it will now be binding on the object to define all properties specified by the Interface.

Another object with the following signature is still considered NDark because its size or signature treats that object.

In the above example, the NDark Interface includes two properties firstName and lastName. It also consists of a method declaration timeTravel() using an arrow function with a string return type.

Any object of type NDark must define the two properties and one method.

Now, compile the app.ts file.

tsc app.ts

On compiling, it will generate the following JavaScript code.

var jonas = {
  firstName: "Jonas",
  lastName: "Kanwald",
  timeTravel: function () { return "Sic Mundus Creatus est"; }
};

console.log(jonas.firstName);
console.log(jonas.lastName);
console.log(jonas.timeTravel());

Output

Jonas
Kanwald
Sic Mundus Creatus est

Interface as Type

TypeScript Interface can be used to define a type and also to implement it in the class. For example, let’s define an interface that types dictionaries with key-value pairs.

// app.ts

interface KeyPair {
  key: number;
  value: string;
}

let kp1: KeyPair = { key: 11, value: "Millie" }; // OK

let kp2: KeyPair = { key: 11, val: "Millie" }; // Compiler Error: 'val' doesn't exist in type 'KeyPair'

let kp3: KeyPair = { key: 11, value: 1888 }; // Compiler Error:

In the above code, the interface KeyPair includes two properties, key, and value. In addition, a variable kp1 is declared as a KeyPair type.

So, it must follow the same data structure as KeyPair. It means only the object with properties key of number type and value of string type can be assigned to a variable kp1.

The TypeScript compiler will show the error if there is any change in the name of the properties or if the data type is different than KeyPair.

Another variable, kp2, is also declared as a KeyPair type, but the assigned value is val instead of value, so this will cause the error.

In the same way, kp3 assigns a number to the value property so that the compiler will show the error.

Thus, TypeScript uses an interface to ensure the proper structure of an object.

Interface for Array Type

To define an array-type Interface,  you must define the index type and values.

// app.ts

interface INumArray {
  [index: number]: number
}

let numArr: INumArray = [1, 2, 3]
console.log(numArr)

Output

tsc app.ts
node app
[ 1, 2, 3 ]

In the above example, interface INumArray defines a type of array with index as a number and value as a number type.

Union Type and Interface

In TypeScript, a union type allows you to specify that a value can be one of several types. For example, the pipe symbol (|) can create a union type. Interfaces can be combined with union types to create more flexible and expressive definitions.

// app.ts

interface match {
  team: string;
  data: string[] | string | (() => string);
}

//data as string 
var options: match = { team: "KylieCostmetics", data: "Kylie" };
console.log(options.data)

//data as a string array 
options = { team: "KylieCostmetics", data: ["Kylie", "Jenner"] };
console.log(options.data[0]);
console.log(options.data[1]);

//data as a function expression 
options = { team: "KylieCostmetics", data: () => { return "**Kylie Jenner**"; } };

var fn: any = options.data;
console.log(fn());

Output

➜ tsc app.ts
➜ node app
Kylie
Kylie
Jenner
**Kylie Jenner**

Interfaces and Inheritance

Other interfaces can extend an interface. In other words, the Interface can inherit from other interfaces. Typescript enables the Interface to inherit from multiple interfaces.

You can use the extends keyword to implement inheritance among interfaces.

interface IDark {
  name: string
}

interface IMartha extends IDark {
  instrument: string
}

var tt = <IMartha>{};
tt.name = "Jonas"
tt.instrument = "Time Clock"
console.log("name: " + tt.name)
console.log("Instrument: " + tt.instrument)

Output

➜ tsc app.ts
➜ node app
name: Jonas
Instrument: Time Clock

Multiple Interface Inheritance in TypeScript

Multiple inheritance has more than one base class and a single derived class. In our case, it is an interface with more than one parent interface and a single-child interface.

// app.ts

interface IDark {
  name1: string
}

interface IStrangerThings {
  name2: string
}

interface ISciencFi extends IDark, IStrangerThings { }

var Iobj: ISciencFi = { name1: 'Martha', name2: 'Eleven' }
console.log(`value 1 is ${Iobj.name1} and value 2 is ${Iobj.name2}`)

Output

➜ tsc app.ts
➜ node app
value 1 is Martha and value 2 is Eleven

The object Iobj is of the type of Interface IScienceFi. The Interface IScienceFi, by inheritance, now has two attributes: name1 and name2, respectively. Hence, the object Iobj must now include these properties.

Optional Property in TypeScript Interface

To define optional properties, mark any property with a “?”. In such cases, objects of an Interface may or may not define these properties.

// app.ts

interface INetflix {
  series: string;
  seasons: number;
  lead?: string;
}

let show1: INetflix = {
  series: 'Stranger Things',
  seasons: 3,
  lead: 'Millie Bobby Brown'
}

console.log(show1)

let show2: INetflix = {
  series: 'Dark',
  seasons: 3,
}

console.log(show2)

Output

➜ tsc app.ts
➜ node app
{ series: 'Stranger Things', seasons: 3, lead: 'Millie Bobby Brown' }
{ series: 'Dark', seasons: 3 }

In this example, we have defined an interface called INetflix.

The INetflix interface accepts two required properties and one optional property.

Series and Seasons are required properties, and the lead is optional.

In the above example, the lead property is marked with ?, so objects of INetflix may or may not include this property.

Then we created two objects from the Interface called show1 and show2.

In the show1 object, we have included the lead property, and in the show2 object, we have not included the lead property.s.

Read-only Properties in TypeScript Interface

TypeScript provides a way to mark a property as read-only. This means that a property cannot be changed once it is assigned a value!

// app.ts

interface IDark {
  name: string;
  readonly year: number
}

let IObj: IDark = {
  name: 'Jonas',
  year: 2020
}

console.log(IObj.name)
console.log(IObj.year)

// Try to modify the property values
IObj.name = 'Martha';
IObj.year = 1888

console.log(IObj.name)
console.log(IObj.year)

Output

tsc app.ts
app.ts:16:6 - error TS2540: Cannot assign to 'year' because it is a read-only property.

16 IObj.year = 1888
 ~~~~


Found 1 error.

In the above example, the year property is read-only. Next, we define the IObj object of type IDark and assign values to the two interface properties.

Next, we try to modify the values assigned to both the properties: name and year. The TypeScript compiler will show the error when we modify the read-only year property.

If you use Visual Studio Code, you will get the error before compilation and in the code editor.

Implementing an Interface

TypeScript interface can be implemented with a Class. The class implementing the Interface needs to conform to the structure of the Interface strictly.

// app.ts

interface IDark {
  name: string
  year: number
  getYear: () => number
}

class Jonas implements IDark {
  name: string
  year: number

  constructor(name: string, year: number) {
    this.name = name
    this.year = year
  }

 getYear(): number {
   return this.year
 }
}

let cycle = new Jonas('Adam', 2053)
console.log(cycle.getYear())

Output

➜ tsc app.ts
➜ node app
2053

In the above example, the IDark Interface is implemented in the Jonas class using the implements keyword.

The implementing class should strictly define the methods and properties with the same name and data type.

The compiler will throw an error if the implementing class does not follow the interface structure.

Of course, the implementing class can define extra functions and properties, but at least it must represent all the members of the Interface.

That’s it.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.