Use of TypeScript Generics

Use of TypeScript Generics

Preface:

In JavaScript , encapsulating an API can have many uses because it is a weakly typed language, but the final result obtained just because of the weak typing is not what we want.

The emergence of TypeScript just solves this problem, but when considering the reuse of API, TypeScript does not seem to be so flexible. At this time, you can use the any type to solve the problem of inflexibility, but it returns to the problem in JavaScript, and the final result may not be what you expect.

To solve this problem, TypeScript introduced the concept of generics. When using generics, you don't need to specify the specific type in advance when defining functions, interfaces, or classes. Instead, you can specify the type when using it. The purpose of this is to reuse our code to a greater extent.

1. Easy to use

Now we need to define a join function, which mainly accepts two values ​​of the same type and returns the concatenated value of the two parameters. The sample code is as follows:

// The so-called generics, in a more popular way, are types that are generally referred to. // Define a join function that accepts two parameters of the same type and returns the concatenated value.
function join<T>(first: T, second: T) {
  return `${first}${second}`
}
// Here, T is specified as string typejoin<string>('第一', '第二') //第一第二// Here, through type inference, the compiler will automatically infer the type based on the passed parametersjoin(1, 2) // 12

Generics are defined using angle brackets <>. When we define the join function, we don't know what types can be accepted, but we can be sure that the two types must be the same. If we want to meet such a requirement, it is not so easy to solve it without generics.

When calling a function, there are two ways to use it. One is to directly specify the type as string type; the other is to use type inference. The editor will automatically help us determine the type based on the passed in parameters.

2. Using generics in functions

When defining a function, we can use multiple generics, and the return value type can also be specified through generics, as long as the quantity and usage methods correspond.

The sample code is as follows :

function identity<T, Y, P>(first: T, second: Y, third: P): Y {
  return second
}
//Specified type identity<boolean, string, number>(true, 'string', 123) // string // Type inference identity('string', 123, true) // true

3. Using Generics in Classes

We can use generics not only in functions but also in classes.

The sample code is as follows:

class DataManager<T> {
  // Define a class with a private array of type T constructor(private data: T[]) {}
  // Get the value in the array based on the index getItem(index: number): T {
    return this.data[index]
  }
}
const data = new DataManager(['一碗周'])
data.getItem(0) // A bowl of Zhou

Moreover, generics can also inherit from an interface. The sample code is as follows:

interface Item {
  name: string
}
class DataManager<T extends Item> {
  // Define a class with a private array of type T constructor(private data: T[]) {}
  // Get the value in the array based on the index getItem(index: number): string {
    return this.data[index].name
  }
}
const data = new DataManager([{ name: '一碗周' }])
data.getItem(0) // A bowl of Zhou

Use extends to achieve the effect of a generic constraint. For the code above, we must constrain the passed value to have a name attribute, otherwise an exception will be thrown.

4. Using type parameters in generic constraints

Suppose there is the following requirement: we define a class with a private object in it, which contains some properties; then we define a method to get the corresponding value through the key.

The implementation code is as follows:

// Define an interface interface Person {
  name: string
  age: number
  hobby: string
}
// Define a class class Me {
  constructor(private info: Person) {}
  getInfo(key: string) {
    return this.info[key]
  }
}
const me = new Me({
  name: 'A bowl of Zhou',
  age: 18,
  hobby: 'coding',
})
// Calling me.getInfo() may result in an undefined value. For example, me.getInfo('myName') // undefined

In the above code, if we call the getInfo() method in the instance object and pass in a non-existent property, we will get undefined. It is not the style in TypeScript to call a method that returns undefined .

This problem can be solved by the keyof operator, which can be used to obtain all keys of a certain type, and its return type is a union type.

The sample code is as follows:

type myPerson = keyof Person // 'name' | 'age' | 'hobby'

Now we can use this operator to solve the problem above.

The sample code is as follows:

class Me {
  constructor(private info: Person) {}
  // This is the same as getInfo<T extends keyof Person>(key: T): Person[T] {
    return this.info[key]
  }
  // getInfo<T extends 'name' | 'age' | 'hobby'>(key: T): Person[T] {
  // return this.info[key]
  // }
}
const me = new Me({
  name: 'A bowl of Zhou',
  age: 18,
  hobby: 'coding',
})
// Calling me.getInfo() will result in a compilation error if an unknown property is passed me.getInfo('myName') // error: Argument of type '"myName"' is not assignable to parameter of type 'keyof Person'.

Now if we access a property that does not exist in the object, the compilation will be abnormal.

This is the end of this article about the use of TypeScript generics. For more related TypeScript generics content, please search for previous articles on 123WORDPRESS.COM or continue to browse the related articles below. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • How to explain TypeScript generics in a simple way
  • TypeScript generic parameter default types and new strict compilation option
  • In-depth understanding of Typescript generic concepts in the front end
  • TypeScript generic usage and generic interface combination

<<:  The difference and choice between datetime and timestamp in MySQL

>>:  Summary of several commonly used CentOS7 images based on Docker

Recommend

8 Reasons Why You Should Use Xfce Desktop Environment for Linux

For several reasons (including curiosity), I star...

MySQL 8.0.18 stable version released! Hash Join is here as expected

MySQL 8.0.18 stable version (GA) was officially r...

Deep understanding of line-height and vertical-align

Several concepts Line box: A box that wraps an in...

How to insert batch data into MySQL database under Node.js

In the project (nodejs), multiple data need to be...

In IIS 7.5, HTML supports the include function like SHTML (add module mapping)

When I first started, I found a lot of errors. In...

How to use Docker container to access host network

Recently, a system was deployed, using nginx as a...

JS implements a simple todoList (notepad) effect

The notepad program is implemented using the thre...

Vue + element dynamic multiple headers and dynamic slots

Table of contents 1. Demand 2. Effect 3. All code...

Vue.js implements image switching function

This article shares the specific code of Vue.js t...

Solution to MySQL replication failure caused by disk fullness

Table of contents Case scenario Solving the probl...

In-depth understanding of the use of r2dbc in MySQL

Introduction MySQL should be a very common databa...

Explanation of Truncate Table usage

TRUNCATE TABLE Deletes all rows in a table withou...

Can Docker become the next "Linux"?

The Linux operating system has revolutionized the...