preface

Work to see many students for the use of ts still have limitations, including the complex type and type of operation, lead to write any code is everywhere, maintenance, basic no iteration code, basic guess method name, parameter types on documentation, completely lost the ts as a strongly typed language. This article will expand on generics and various types of dark magic, add to your TS toolkit, and write ts code that is more friendly to you and other developers

Part I: Generic Types

Definition :(from baidu.com)

Generics are a feature of programming languages. Allows programmers writing code in strongly typed programming languages to define variable parts that must be specified before use. Support for generics varies from programming language to programming language and from compiler to runtime environment. A data type parameterized for code reuse to improve software development efficiency. Generic classes are reference types and heap objects, mainly introducing the concept of type parameters.

Uh, what is this stuff? I’m sure most people will be confused by this definition.

Definition :(from myself)

Generics are simply programming for types, transferring some of the types that cannot be determined at authoring time to the runtime

A very common example is code like this:

Array<T>
Copy the code

Before we process a function, we only know that the parameter is an Array type, but we don’t know what type the Array contains. What type should we use to describe the object?

withany?

This is obviously inappropriate because we know that this type must have functions like array.prototype.reduce (), array.prototype.map (), etc., hanging from the Array prototype chain. We just don’t know array.prototype.map ((… Args) => {}).

withArray<any>?

Array.prototype.map((… If args => {}), args will be of type any. We want to have a way to define only an Array, and the contents of the Array will be determined at runtime.

Array<T>

The answer is in the puzzle, and our final choice is Array

where T is generic, a type that cannot be determined at compile time.

Usage scenarios

A simple case

When you happily put Array into your code, vscode will enthusiastically pour cold water on you:

export function test(arr: Array<T>) {
  return arr
}
Copy the code

The solution is simple:

As we mentioned earlier, generics can be understood as programming for types, and in this case T is not a specific type, but a variable, so we just have to declare it in advance

// The Angle bracket form is declared after the method name
export function test<T> (arr: Array<T>) {
  return arr
}
Copy the code

The default value

export function test<T = string> (arr: Array<T>) {
  return arr
}
Copy the code

Similar to the default value of the argument, T is automatically recognized as string when it is used without providing the type of T

Type inheritance: (extends keyword)

Imagine a scenario where our test function needs to manipulate Array data, but we already have some knowledge of the type of T.

For example, our test method operates on a property called size in T.

At this point we can use:

interface Sizable {
  size: number
}

export function test<T extends Sizable> (arr: Array<T>) {
  // there will be code completion for i.size.
  arr.map(i= > i.size)
  return arr
}

// This line of code will tell you that 'ese' does not contain the. Size attribute.
test(['ese'])
Copy the code

Part TWO: Type black magic

This part is a summary of my daily TS use of small skills and partial door API, I hope to make you get twice the result with half the effort.

The constructor of a class:

Scene:

You designed a create function using factory patterns to proxy the creation of classes (using common examples such as singleton patterns, class reference counting etc..).

In this case, you naturally want to pass the class T you need as a generic, but in this case, the create function argument type should be T constructor (t. constructor). As follows:

Implementation:

export function create<T> (ctor: { new (): T }) :T {
  return new ctor()
}
Copy the code

The dark magic is here. {new (): T} represents an object that can be new and returns type T, that is, a constructor of T

Mapping types (built-in in TypeScript)

Scene:

The Employee object is a type in a back-end database with several properties:

export interface Employee {
  age: number
  sex: 'male' | 'female'
  salary: number
  mood: 'happy' | 'sad' | 'average'
  workTime: '996' | '965' | '995' | '1027'
}
Copy the code

If we wanted to update the Mood property of one Employee, the TS compiler would report an error telling us that some of the mood properties were missing

Implementation:

Wrap Employee with a Partial type to get an Employee with all optional parameters. The modifier)

type Partial<T> = { [P inkeyof T]? : T[P] |undefined }
Copy the code

His implementation is also not complicated. He takes a generic T and outputs a new type, in which each property is a property of T plus ‘? ‘modifier, where keyof T represents the name of the property in the T generic and T[P] represents the type of the corresponding property

From this form, we can deduce various similar tool types:

type Required<T> = { [P inkeyof T]-? : T[P] |undefined }
Copy the code
type Readonly<T> = { readonly [P in keyof T]: T[P] | undefined }
Copy the code

Extract base types from complex types

Scene:

Axios is certainly familiar as a promise-based HTTP request library. His request return objects are wrapped in Promise.

At this point, we might have a function that needs to process the body of the response returned by the Promise, that is, we want to extract the type of AxiosResponse from the Promise<AxiosResponse> type.

Implementation:

type Depromisify<T> = T extends Promise<infer U> ? U : T
Copy the code

Looks a little complicated? Let’s go through it step by step:

  • extends: used to determine whether T generics can inherit the Promise type
  • T ? T : unknownJs common ternary operators, ts is the same effect
  • infer: tells the TS compiler, as the name suggests, that it needs to reason that the type here exists in the type variable U

In a similar way, we can implement more tool types:

// If T is an array, the type of the object in the array is extracted
type FlattenArray<T> = T extends (infer U)[] : U : T
Copy the code
// Extract the return type of the function
type ReturnType<T> = T extends(... args:any[]) => infer U ? U : T
Copy the code

, etc.

Afterword.

TypeScript, as a strongly typed superset of JavaScript, retains the flexibility of dynamically typed languages (any) while offering the benefits of a variety of strongly typed languages that are worth learning more about. Hope this article can write TS code to help everyone, get rid of AnyScript as soon as possible!

The original link