When learning ts source code, I found that I still couldn't understand many generics, so I wanted to write an article to summarize the commonly used generics. Basic essential knowledge
Union type vs cross type
// Union type interface Bird { name: string; fly(): void; } interface Person { name: string; talk(): void; } type BirdPerson = Bird | Person; let p: BirdPerson = { name: "zfeng", fly() {} }; let p1: BirdPerson = { name: "zfeng", talk() {} };
The union type uses "|" to represent the relationship between or, and only one of them can be satisfied.
interface Bird { name: string; fly(): void; } interface Person { name: string; talk(): void; } type BirdPerson = Bird & Person; let p: BirdPerson = { name: "zhufeng", fly() {}, talk() {} };
The cross type uses "&" to represent the relationship between and. It needs to meet all conditions.
Built in condition type
type Extract<T, U> = T extends U ? T : never; type Exclude<T, U> = T extends U ? never : T; type NonNullable<T> = T extends null | undefined ? never : T; type N = NonNullable<string | number | null | undefined>;// Delete null and undefined; type E = Exclude<string | number, string>; // Exclude relationship output string; type I = Extract<string | number, string>; // Include relationship output number;
Type inference of function
Gets the type of the return value of the function
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any; function getUserInfo(name: string, age: number) { return { name, age }; } type UserInfo = ReturnType<typeof getUserInfo>; const userA: UserInfo = { name: "zhufeng", age: 10, };
Gets the type of the function parameter
type Parameters<T> = T extends (...args: infer R) => any ? R : any; function getUserInfo(name: string, age: number) { return { name, age }; } type T1 = Parameters<typeof getUserInfo>; // [name: string, age: number]
Generic advanced
Many people's understanding of generics still stays at the basic level. I'll understand what generics are from the perspective of collection.
Case 1: field extraction
Given an interface person, there are three fields: name,age and visible. The current requirement is to get a new interface with only name and age. Common ideas of ordinary people:
interface Person { name: string; age: number; visiable: boolean; } interface Person1 { name: string; age: number; }
We can meet the requirements by writing an interface. But this way of writing seems very redundant. In fact, ts provides a method that we can implement. Let's take a look at the example.
Method 1: Pick to extract fields

// pick principle // type Pick<T, K extends keyof T> = { [P in K]: T[P] }; interface Person { name: string; age: number; visiable: boolean; } type Person1 = Pick<Person, 'name'|'age'> ;
Person 1 contains name and age fields.
Method 2: Omit reverse acquisition

interface Person { name: string; age: number; visiable: boolean; } type Exclude<T, U> = T extends U ? never : T; type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; type Person2 = Omit<Person, "age">;
Case 2: operation of two interfaces
We treat an interface as a set, and the operations of the two sets mainly include Union, intersection and difference.
intersection

type Extract<T, U> = T extends U ? T : never; type Intersection<T extends object, U extends object> = Pick< T, Extract<keyof T, keyof U> & Extract<keyof U, keyof T> >; type C1 = { name: string; age: number; visible: boolean }; type C2 = { name: string; age: number; sex: number }; type C3 = Intersection<C1, C2>;
Definition of Intersection: for a given two sets, return a new set containing elements common to the two sets. Through Intersection, you can obtain a new interface. C3 only contains name.age. As shown above.
Difference set

type Exclude<T, U> = T extends U ? never : T; type Diff<T extends object, U extends object> = Pick< T, Exclude<keyof T, keyof U> >; type C1 = { name: string; age: number; visible: boolean }; type C2 = { name: string; age: number; sex: number }; type C11 = Diff<C1, C2>;
Definition of difference set: for a given two sets, return a new set containing all elements that exist in the first set and do not exist in the second set. By implementing the difference set through Diff, a new interface can be obtained. The interface is only visible. As shown above.
Union

Definition of Union: for a given two sets, a new set containing all elements in the two sets is returned. A new interface can be obtained by merging. The interface contains all the attributes of C1 and C2. As shown above.
//Compute is used to merge cross types type Compute<A extends any> = A extends Function ? A : { [K in keyof A]: A[K] }; type Omit<T, K> = Pick<T, Exclude<keyof T, K>>; type Merge<O1 extends object, O2 extends object> = Compute< O1 & Omit<O2, keyof O1>>; type C1C2 = Merge<C1, C2>;
Special case: Overwrite
type C1 = { name: string; age: number; visible: boolean }; type C2 = { name: string; age: string; sex: number };
C1 and C2 are merged. There is age in C1, the type is number, and there is age in C2, the type is string. After merging, is age string or number?
Overwrite generics solve the problem of who overwrites who.

type C1 = { name: string; age: number; visible: boolean }; type C2 = { name: string; age: string; sex: number }; type Overwrite< T extends object, U extends object, I = Diff<T, U> & Intersection<U, T> > = Pick<I, keyof I>; type overwrite = Overwrite<C1, C2>;