This is the 28th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

Hello everyone, I am a bowl week, a front end that does not want to be drunk (inrolled). If I am lucky enough to write an article that you like, I am very lucky

Writing in the front

In JavaScript, encapsulating an API can be used for a variety of purposes, because it’s a weakly typed language, but because it’s weakly typed you don’t end up with what you want.

TypeScript addresses this problem, but it’s not as flexible when it comes to API reuse. At this point, you can use any to solve the inflexibility problem, but back to the JavaScript problem, the end result may not be what you expected.

To address this situation, TypeScript introduces the concept of generics, which allows us to define functions, interfaces, or classes without specifying specific types up front, but specify them when we use them. This allows us to reuse our code to a greater extent.

Simple use

Now we define a join function that accepts two values of the same type and returns the concatenated value of the two parameters. Example code is as follows:

// By generics, we mean a generic type
// Define a join function that takes two arguments of the same type and returns the concatenation of the two parameters.
function join<T> (first: T, second: T) {
  return `${first}${second}`
}
// T is a string
join<string> ('the first'.'2') // First and second
// In this case, the compiler automatically inferred the type from the parameters passed in
join(1.2) / / 12

Copy the code

Generics are defined by <> Angle brackets. When we define the join function, we do not know which types we can accept, but we do know that the two types must be the same. If we want to satisfy this requirement, it is not so easy without generics.

There are two ways to call a function: one is to specify the type as string directly; The other is through type inference, where the editor automatically helps us determine the type based on the parameters passed in.

Use generics in functions

When defining a function, we can use multiple generics, and the return value type can be specified by generics, as long as the number and usage match. Example code is as follows:

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


Copy the code

Use generics in classes

We can use generics not only in functions, but also in classes. Example code is as follows:

class DataManager<T> {
  // Define a class that has a private array of type T
  constructor(private data: T[]) {}
  // Say the values in the array according to the index
  getItem(index: number): T {
    return this.data[index]
  }
}
const data = new DataManager(['Bowl Week'])
data.getItem(0) / / a bowl of weeks

Copy the code

Generics can also inherit from an interface, as shown in the following example:

interface Item {
  name: string
}
class DataManager<T extends Item> {
  // Define a class that has a private array of type T
  constructor(private data: T[]) {}
  // Say the values in the array according to the index
  getItem(index: number) :string {
    return this.data[index].name
  }
}
const data = new DataManager([{ name: 'Bowl Week' }])
data.getItem(0) / / a bowl of weeks

Copy the code

Using extends serves the purpose of a generic constraint. In the case of the above code, we must constrain that the value passed must have a name attribute, or else an exception will be thrown.

Use type parameters in generic constraints

Suppose we define a class with a private object that contains attributes. Then define a method that retrieves its corresponding value via 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: 'Bowl Week'.age: 18.hobby: 'coding',})// Calling me.getInfo() may get undefined as shown in the following example
me.getInfo('myName') // undefined

Copy the code

In the above code, if we pass in a missing property when we call the getInfo() method on the instantiation object, we get undefined. Calling a method that returns undefined is not the TypeScript style.

This problem can be solved by using the keyof operator, which can be used to get all keys of a type whose return type is the union type. Example code is as follows:

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

Copy the code

Now you can use this operator to solve the problem above. Example code is as follows:

class Me {
  constructor(private info: Person) {}
  // This is the same as the following
  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: 'Bowl Week'.age: 18.hobby: 'coding',})// Calling me.getInfo() will compile an error if an unknown attribute is passed
me.getInfo('myName') // error: Parameters of type "myName" cannot be assigned to parameters of type "keyof Person".

Copy the code

Now we just need to access properties that don’t exist in the object and the compiler will get an exception.

Phase to recommend

  • Summary of 42 front-end layout schemes – Juejin (juejin. Cn)
  • JS advanced must be 5 high order functions – nuggets (juejin. Cn)
  • 6 types of inheritance before ES6 – Digging gold (juejin.cn)
  • – Juejin (juejin. Cn)