Enforcing a Class Implementation in TypeScript — Introduction to TypeScript Interfaces
Tags: typescript, javascript, tutorial, webdev
Published: 2020-02-02 22:19:02

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

**Follow me on Twitter at https://twitter.com/AuMayeung**

**Many more articles at https://medium.com/@hohanga**

**Even more articles at http://thewebdev.info/**

The big advantage of TypeScript over plain JavaScript is that it extends the features of JavaScript by adding type safety to our program’s objects. It does this by checking the shape of the values that an object takes on. Checking the shape is called duck typing or structural typing.

Interfaces are one way to fill the role of naming data types in TypeScript. It’s very useful for defining a contract within our code in TypeScript programs. In this article, we will look at how we use interfaces for enforcing the implementation of classes.

Class Types
===========

We can use TypeScript interfaces to make sure classes meet a specific contract as specified by interfaces like in other programming languages such as C# and Java. For example, we can define a class that implements the items specified by the interface with the `implements` keyword like we do in the code below:

```typescript
interface PersonInterface {  
  name: string;  
}

class Person implements PersonInterface {  
  name: string = 'Mary';      
}
```

In the code above, we implemented the class `Person` which implements the items outlined in the `PersonInterface`. Since the `PersonInterface` has the field `name` which is a string, we follow that contract in the `Person` class that we implemented below that. The code above implements all the items outlined in the interface, so the TypeScript compiler will accept the code above as being valid. If we didn’t implement the items in the interface but we used the `implements` keyword, then we’ll get an error:

```typescript
interface PersonInterface {  
  name: string;    
  age: number;    
}

class Person implements PersonInterface {      
  name: string = 'Mary';    
  foo: any = 'abc';  
}
```

For example, if we have the code above, then the TypeScript compiler will reject it with an error since we only have the `name` field in the `Person`, but instead of the `age` field, we have `foo` instead. So if we try to compile the code above, we would get an error message “Class ‘Person’ incorrectly implements interface ‘PersonInterface’. Property ‘age’ is missing in type ‘Person’ but required in type ‘PersonInterface’.”

However, after we implemented all the items in the interface, we can add extra items that aren’t specified in the interface and the TypeScript compiler would accept the code:

```typescript
interface PersonInterface {  
  name: string;      
}

class Person implements PersonInterface {      
  name: string = 'Mary';    
  age: number = 20;  
}
```

The code above would be accepted by the TypeScript compiler since we have all the things in the interface and we added stuff to the class which isn’t in the interface, which is acceptable in TypeScript.

We can also describe methods that we will implement in classes with an interface. To do this, we can add method signatures along with their return type in our interfaces, like in the code below:

```typescript
interface PersonInterface {  
  name: string;  
  age: number;  
  setName(name: string): string;  
  setAge(age: number): number;  
}

class Person implements PersonInterface {      
  name: string = 'Mary';    
  age: number = 20;  
  setName(name: string) {  
    this.name = name;  
    return name;  
  } setAge(age: number) {  
    this.age = age;  
    return age;  
  }  
}
```

In the code above, we put 2 method signatures that are to be implemented in the class that implements the interface in the `PersonInterface`. We have the method signature for the `setName` method and another one for the `setAge` method. Inside the parentheses of each we specified the required parameters for each method. In the `setName` method, we specified that we require the `name` parameter and is of the type string. In the `setAge` method, we specified that we have an `age` parameter and is of type number. That’s the part on the left of the colon. On the right of the colon, we have the return type of each method. For the `setName` method, we specified that it returns a string. In the signature of the `setAge` method, we specified that it returns a number.

Then in the `Person` class, we just following the implementation as it’s outlined in the interface. So we added the `name` and `age` fields, which we designate as type string and number respectively. Then we add the `setName` and `setAge` methods as we outlined in the `PersonInterface`. Note that the type designation in the parameters of the methods are required, but the return type is not since it can be inferred by TypeScript but the parameter types have to checked explicitly. This means that if we have something like the following code with the parameter type omitted:

```typescript
interface PersonInterface {  
  name: string;  
  age: number;  
  setName(name: string): string;  
  setAge(age: number): number;  
}

class Person implements PersonInterface {      
  name: string = 'Mary';    
  age: number = 20;  
  setName(name) {  
    this.name = name;  
    return name;  
  } setAge(age) {  
    this.age = age;  
    return age;  
  }  
}
```

Then the TypeScript compiler will reject the code with the error messages “Parameter ‘name’ implicitly has an ‘any’ type.(7006)” for the `setName` method and “Parameter ‘age’ implicitly has an ‘any’ type.(7006)”. As we can see from the errors, parameters that have no type designation attached to it will be implicitly designated the `any` type, which isn’t what’s specified in the `PersonInterface` .

Interfaces only describe the public side of classes, rather than both the public and private side. This means that we can’t use it for checking private fields and methods.

Static Side vs Instance Side of Classes
=======================================

In TypeScript, interfaces only check the instance side of classes, so anything that’s related to the static side of classes isn’t checked. For example, if we have the following code:

```typescript
interface PersonInterface {  
  new (name: string, age: number);  
}

class Person implements PersonInterface {      
  constructor(name: string, age: number) {}  
}
```

We would get the error “Class ‘Person’ incorrectly implements interface ‘PersonInterface’. Type ‘Person’ provides no match for the signature ‘new (name: string, age: number)” since the static side of a class can’t be checked by an interface, and the `constructor` is on the static side. Instead we have to add different interfaces for each part of the static side. We can do this like in the code below:

```typescript
interface PersonConstructor {  
  new (name: string, age: number): PersonInterface;  
}

interface PersonInterface {  
  name: string;  
  age: number;  
  greet(name: string): void;  
}

const createPerson = (ctor: PersonConstructor, name: string, age: number): PersonInterface =>{  
  return new ctor(name, age);      
}

class Person implements PersonInterface {    
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  greet(name: string) {
    console.log(`Hello ${this.name}. You're ${this.age} years old.`)
  }
}

let person = createPerson(Person, 'Jane', 20);  
console.log(person);
```

With the code above, the `PersonInterface` only has the public members, which are `name` , `age` , and the `greet` method. This forces us to implement these members in our `Person` class. The `constructor` isn’t checked by the `PersonInterface` since it’s a static member. We leave the check for that in the `createPerson` function. In `createPerson`, we check that the `ctor` parameter implements the `PersonConstructor` correctly, since we have the `new` method with the `name` and `age` parameters in the signature and we checked that it returns a `PersonInterface` which means that the constructor is returning an instance of some class that implements the `PersonInterface` . The code above will be accepted by the TypeScript compiler since we checked the static side separately from the instance side since we created the `createPerson` to let us check the static side, which is the `constructor` .

Another powerful feature of TypeScript is that we can use interfaces to check what we implemented in our classes by using interfaces. It’s useful for checking non-static members of a class. However, for static members like `constructor` , we have to check them outside the interface that we use for implementing classes since they can’t check for static members like the `constructor` method.