1. Introduction

This article focuses on TypeScript interface declarations and generics, which are commonly used in real projects, so it’s a separate article.

For those of you who want to start learning TypeScript from 0, I highly recommend my previous post:

Encounter TypeScript and build a simple TS development environment – gold digging

Initial TypeScript, understanding types and type manipulation – digging gold

Dig deep into TypeScript functions – nuggets of gold

In this article, you will learn:

1. Learn how to use interfaces; 2. Understand the difference between an interface and a type alias. 3. Learn how to use generics. 6. Etc.Copy the code

2. The interfaceinterface

An interface declaration is another way to name object types:

interface IInfo { name: string age? : number // Optional type} const info: IInfo = {name: 'James ', age: 20} info.age // ok info.name // okCopy the code

TypeScript only cares about the structure of the value passed to info) — whether the value has the desired properties. It is this focus on type structure and power that makes TypeScript a structured type system.

The difference between type aliases and interfaces:

Type aliases are very similar to interfaces, and most of the time, you can use them any way you want. Almost all features of an interface can be used in Type. The key difference is that the type alias itself cannot add new attributes, whereas the interface can be extended:

Interface Person {name: string} Interface Student extends Person {stuNo: string } const stu: Student = { name: 'hsh', stuNo: '01'} stu.name // ok stu.stuno // ok // Type alias extends type Person = {name: string } type Student = Person & { stuNo: string } const stu: Student = { name: 'hsh', stuNo: '01' } stu.name // ok stu.stuNo // okCopy the code

An interface can also add new attributes to an existing interface, whereas type cannot:

Interface Message {content: string} interface Message {contentType: string} const MSG: {contentType: string} Message = { content: 'message', contentType: 'text'} msg.content // ok msg.contentType // ok // type // Error: identifier "Message" repeated type Message {content: string } type Message { contentType: string }Copy the code

So in the actual development process, how to choose to use type or interface? Here’s what the official document says:

For the most part, you can choose based on personal preference, and TypeScript will tell you if it needs something to be the other kind of declaration. If you would like a heuristic, use interface until you need to use features from type.

Most of the time, you can choose according to your personal preference. TypeScript tells you if it needs declarations in other ways. If you prefer exploratory use, use interface until you need to use the Type feature.

2. The generic

Let’s start with what the official documentation says about generics:

A major part of software engineering is building components that not only have well-defined and consistent APIs, but are also reusable. Components that are capable of working on the data of today as well as the data of tomorrow will give you the most flexible capabilities for building up large software systems.

In languages like C# and Java, one of the main tools in the toolbox for creating reusable components is generics, that is, being able to create a component that can work over a variety of types rather than a single one. This allows users to consume these components and use their own types.

An important part of software engineering is building components that not only need well-defined and consistent apis, but also need to be reusable. Good components that are compatible not only with today’s data types, but also with future data types, give you maximum flexibility when building large software systems.

In languages such as C# and Java, the tools for creating reusable components are called generics. With generics, we can create a component that supports many types, which allows users to customize these components with their own types.

The definition of generics in TypeScript: when you define a function, you don’t determine the type of the function’s parameters. Instead, you let the caller tell you what type the parameters of the function should be.

Before we use generics, we write the type of the function like this:

function identity(arg: any): any {
  return arg;
}
Copy the code

While using any lets us accept any type of ARG argument, it also lets us lose the type information when the function returns. If we pass in a number, the only information we know is that the function can return any type of value.

So we need a way to capture the type of the parameter and then use it to represent the type of the return value.

function identity<Type>(arg: Type): Type {
  return arg;
}
Copy the code

This Type allows us to capture the Type provided by the caller so that it can be used later, and as you can see, the return value also uses this Type. That’s what generics are. The types of function arguments and return values are not fixed, but are determined by the caller of the function.

We can call the above generic function in two ways:

  • Pass in all parameters (including type parameters)

    let output = identity<string>("myString");  // output: string
    Copy the code

Here, we wrap the argument with <> instead of () and explicitly set Type to string as an argument to the function call;

  • Using type inference

    let output = identity("myString"); // let output: string
    Copy the code

Instead of passing in an explicit <> Type, the compiler automatically sets Type to its Type (string) when it sees the value myString.

Use of generic interfaces

Generics can also be used in interfaces:

Interface User<T, N> {name: T, age: N} const User: User<string, number> = {name: 'HSH ', age: 18 } user.name // ok user.age // okCopy the code

Generic constraint

Sometimes you need to put some Type constraints on Type to make it through compilation.

Suppose we now have a getLength function:

function getLength<T>(arg: T) { console.log(arg.length); // Error: attribute "length" does not exist on type "T"}Copy the code

If the Type is number, there is no length attribute. If the Type is number, there is no length attribute. If the Type is number, there is no length attribute.

Function getLength<T extends ILength>(arg: T) {console.log(arg.length); } getLength('111') // ok getLength(['111']) // ok // getLength(111) // Error: Arguments of type "number" cannot be assigned to arguments of type "ILength"Copy the code

Now that the generic function is constrained, it no longer applies to all types, and we need to pass in values that match the constraint.

3. Summary

In this article, I’ve familiarized myself with the use of interfaces and generics in TypeScript. I recommend TypeScript4 as a Chinese version of TypeScript.