TypeScript – Interface (I)

I found the concept of Interface is quite fun, cause no matter what kind of languages, e.g. Golang, Java, Kotlin, etc. Even Swift/Objective-C also provide similar concept protocol to support the polymorphism. Then, let’s check what power TS interface gets.

One of TypeScript’s core principles is that type checking focuses on the shape that values have. This is sometimes called “duck typing” or “structural subtyping”. In TypeScript, interfaces fill the role of naming these types, and are a powerful way of defining contracts within your code as well as contracts with code outside of your project.

Let’s check an example first.

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

const mySelf = { name: 'chauyan', occupation: 'software engineer'}; 
printName(mySelf);

This example is pretty straightforward. Create a function call printName with a person parameter which’s type is {name: string}, we want to print this person’s name. When you want to use this function, you need to create an object which contains the name property and we can print this object’s name. Here comes several problems when using this way in a function:

  1. If we want to add more limitations to this person parameter to tell what kind of people can use this printName function, it will make the parameter type too complicated.

  2. We reveal too much detail for this passed in parameter person. It’s not necessary to show the detail for a function inside usage. If we start considering the modularization that would be another issue.

  3. It’s not convenient for the code reuse. Cause we can’t reuse this printName function in a simple way. Instead, we need to do more effort to make this function reusable.

Do we have any better solution for this? Yes, we can use interface as a substitute. Let’s check.

interface Info { 
  name: string;
}

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

const mySelf = { name: 'chauyan', occupation: 'software engineer'}; 
printName(mySelf);

Create an interface called Info that contains a property named name that is of the type string. We don’t have to explicitly say that the object we pass to printName implements this interface like we might have to in other languages. Here, it’s only the shape that matters. If the object we pass to the function meets the requirements listed, then it’s allowed. It’s worth pointing out that the type checker does not require that these properties come in any sort of order, only that the properties the interface requires are present and have the required type.

You might know if a property with a question maker means that property is optional, for example:

interface Info { 
  name: string;
  team?: string;  
}

In this example, the interface Info has an optional property named team which means you don’t have to setup a value for it.

interface Info { 
  name: string;
  team?: string;  
}

function login(person: Info): { certificatedName: string; currentDept: string} {
  const group = { certificatedName: "", currentDept: "Product" };
  group.certificatedName = person.name;
  if (person.team == 'mobile') {
    group.currentDept = 'product';
  }
  else if (person.team == 'backend') {
    group.currentDept = 'infra';
  }
  return group;
}

const user = login({name: 'chauyan'});

console.log(user);

In this example, you can see I only send an object with only one property called name. The login function will automatically generate the department for me even I don’t fill in the team property. Here comes an interesting problem: do you still remember the previous example – printName? In that example, we throw in an object with two properties but the interface just needs one, TypeScript will help to do the check and get the necessary one to use. What about the optional property? Does it still work in this way? Let’s check:

interface Info { 
  name: string;
  team?: string;  
}

function login(person: Info): { certificatedName: string; currentDept: string} {
  ...
  return group;
}

const user = login({name: 'chauyan', seatNumber: 1000});

This time it will show the error:

Argument of type '{ name: string; seatNumber: number; }' is not assignable to parameter of type 'Info'.
  Object literal may only specify known properties, and 'seatNumber' does not exist in type 'Info'.

18 const user = login({name: 'chauyan', seatNumber: 1000});

It says you can’t do this, cause seatNumber is not a pre-defined property in this interface. Ok, an optional value actually has several advantages:

  1. You can define a possible value with a pre-defined type in advice.
  2. You can use an optional value to avoid type casting errors, optional values are also called option bags.

So, if you want previous compiled error example to work as you expect. You have two ways.

  1. Force casting to the interface you want:
const user = login({name: 'chauyan', seatNumber: 1000} as Info);
  1. Add an additional handler property:
interface Info { 
  name: string;
  team?: string;  
  [prop: string]: any;
}

For 1, I strongly do not recommend this way. Cause it’s obvious that you might have something wrong here. For 2, it’s a better way to save any non-predefined properties.

Interfaces are capable of describing the wide range of shapes that JavaScript objects can take. In addition to describing an object with properties, interfaces are also capable of describing function types.

To describe a function type with an interface, we give the interface a call signature. This is like a function declaration with only the parameter list and return type given. Each parameter in the parameter list requires both name and type. For example:

interface Logger {
  (title: string, msg: string): void;
}

After you define the interface, you can it in this way:

let printLog: Logger;
printLog = function(title: string, msg: string) {
  console.log(`Title: ${title} - Message : ${msg}`);
}

// or in this way, without specifying the parameter types
printLog = function(title, msg) {
  console.log(`Title: ${title} - Message : ${msg}`);
}

Ok, here is an introduction of TypeScript Interface. Happy Coding.

Leave a comment

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