1. Index type

Keyof index type query operator that can get all the public attribute names on the generic T to form a joint type

class Person {
    name: string = "Mr. Hu"
    age: number = 18
    private UserId: number = 123
}

type PropNames = keyof Person
Copy the code



Note: 

  1. Keyof can only get on genericspublicProperty name, property name string type,
  2. Keyof returns the union type

[] Index type access operator, similar to js’s syntax for accessing an attribute value of an object. In TS, it can be used to access the type of an attribute

class Person {
    name: string = "Mr. Hu"
    age: number = 18
    time: Date = new Date(a)private UserId: number = 123
}

type PropNames = keyof Person // "name"|"age"|"time"

type NameType = Person["name"]

type PropTypes = Person[PropNames]
Copy the code



T[" Attribute name "]The type of an attribute name can be obtained separately



T[keyof T]You can get the combined type of the types of all public properties in the generic T



Now that we have the basics, we can implementpickThe pick function fetches specified properties from an object

function pick<T.K extends keyof T> (obj: T, prop: K) :T[K] {
    return obj[prop]
}
Copy the code

K extends Keyof T refers to the combined type K can assign to all public properties of the generic T. T[K] returns the property type on the generic T

const User = {
    name: "Mr. Hu".age: 18.id: 12345
}
const nameValue = pick(User, "name")/ / hu
Copy the code



As shown in figure

keyof T = "name"|"age"|"id"  

So K = “name” | | “age”, “id”, namely the prop is optional attribute “name” | | “age” “id”

2. Mapping type

The syntax of the mapping type is: [K in Keys], similar to the JS array method forEach, iterates over Keys and assigns values to K. Above we have implemented a pick function, now we want to implement a TS utility type MyPick, which selects the desired attributes from the generic T

// Define a generic T, and to select the desired attributes from T, define K as the union of all T public ** attribute names **
type MyPick<T,K extends keyof T>={
// K is a union type, we need to iterate over K, using the syntax of mapping type [K in Keys]
  [P in K]:T[P]// P is the attribute name, and T[P] is the attribute type
}
Copy the code
type MyPick<T,K extends keyof T>={
    [P in K]:T[P]
}

interface User{
    name:string.age:number.id:number
}

type name = MyPick<User,"name">
Copy the code





There is one in the TS tool typePartial, you can make all types optional

// define a generic T
type MyPartial<T>={
  // keyof T retrieves all pubilc attribute names in the generic T
  // In can iterate over all attribute names and assign them to K
  // then T[K] is the attribute type
  / /? On behalf of the optional
  
  [K inkeyof T]? :T[K] }Copy the code
type User = {
    id: number.name: string
}

type PartialUser = MyPartial<User>
Copy the code



3. Condition type

T extends U ? X: Y Returns type X if T can be assigned to U and type Y otherwise

type IsStringOrNumber<T> = T extends string ? string : number
// The string "123" is passed to T, that is, "123" is returned if it can be assigned to string, otherwise number is returned
type str = IsStringOrNumber<"123">
Copy the code



Pay attention to: is whether T can be assigned to U, not whether T is of type U, since U may be any, a subtype of all types, e.gtype IsStringOrNumber = T extends any ? string : numberAs long as T is any type that can be assigned to any, this expression returns only string

3.1 Conditional types and union types

A conditional type can be combined with a union type to form a distributed conditional type, for example

// Find the type in T that does not exist in U and return it
type Diff<T, U> = T extends U ? never : T

type DiffType = Diff<number | string | boolean.undefined | string>
Copy the code



The code above is equivalent to

type DiffType2 = Diff<number.undefined | string> 
		| Diff<string.undefined | string>
                | Diff<boolean.undefined | string>
Copy the code



When T type is union typenumber | string | boolean, will be separatelynumber | string | boolean

Assign Diff

note:
,u>

  1. Only bare type parameters can implement distributed conditional typing,
  2. Bare type parameters, that is, type parameters cannot be wrapped in other types, such as tuples, tuples, functions, promises, etc
// The generic T is wrapped around [T], so it is no longer a bare parameter
type Diff<T, U> = [T] extends [U] ? never : T

type DiffType = Diff<number | string | boolean.undefined | string>
Copy the code



Distributed conditional typing cannot be implemented when T is wrapped in an array [T]

type DiffType = [number | string | boolean] extends [undefined | string]?never
    	: number | string | boolean 
Copy the code

3.2 Condition types and mapping types

Now we implement a type tool that pulls out the required types

interfaceUser { id? :number
    name: string
    age: number
}


type NullableKeys<T> = {
  	/ / if k is id, T [k] = number | is undefined
	// use undefined extends T[k] to know k is an optional property
    [K inkeyof T]-? :undefined extends T[K] ? never : K
}[keyof T]

type keys = NullableKeys<User>
// [keyof T] = ["id"|"name"|"age"]
Copy the code



Pay attention to :

  1. + -Used in mapping types to add modifiers to attributes, -? -readonly Changes the readable property to non-read-only
  2. If you don’t write-?Although passedundefined extends T[k]The attribute ID is found and its value is assigned to never, but because it is not removed?, TS will default toidaddundefinedThe value of the
type NullableKeys<T> = {
  / / not -?
    [K in keyof T]: undefined extends T[K] ? never : K
}// Note that there is no keyof here

type keys = NullableKeys<User>
Copy the code

As shown, do not add -? ,id is assigned to undefined

type NullableKeys<T> = {
  / / -?
    [K inkeyof T]-? :undefined extends T[K] ? never : K
}// Note that there is no keyof here

type keys = NullableKeys<User>
Copy the code

4. infer

Infer can act as a type variable of a class to be inferred from conditional statements. If we want to know the element types of an array, we might do so

type ElementOf<T> = T extends Array<string>?string : T extends Array<number>?number:...Copy the code

The element type of the array is a variable. We can infer this variable by using infer

type ElementOf<T> = T extends Array<infer E> ? E :never

type strArr= Array<string>

type arrType = ElementOf<strArr>
Copy the code

Read the source code for Utility-types

Github.com/piotrwitek/…

1.IfEquals

You can determine if two types are the same and customize the returned type

type IfEquals<X, Y, A = X, B = never> = (<T>() = > T extends X ? 1 : 2)
	extends 
	<T>() = > T extends Y ? 1 : 2
  	? A
  	: B;
Copy the code

If generic X is the same as generic Y, A is returned, otherwise B is returned

  1. (<T>()=> T extends X ? 1:2)This is an expression and will not be executed
  2. (<T>()=> T extends Y ? 1:2)So this is an expression
  3. If the two expressions are the same, return A, otherwise return B

2.MutableKeys

Gets the combined type of all property names of an object except readonly

/* * @example * type Props = { readonly foo: string; bar: number }; * * // Expect: "bar" * type Keys = MutableKeys
      
       ; * /
      
export type MutableKeys<T extends object> = {
  [P inkeyof T]-? : IfEquals< { [Qin P]: T[P] },
    { -readonly [Q in P]: T[P] },// -readonly changes a read-only attribute to a non-read-only attribute
    P
  >;
}[keyof T];
Copy the code
  1. [Q in P]:T[P] Q is not used, but if [Q in P] is not written, we can only get P, not readOnly
  2. Details you can see this article on zhihu www.zhihu.com/question/36…

3. ReadonlyKeys

Gets the combined type of all readonly property names of an object

/* * @example * type Props = { readonly foo: string; bar: number }; * * // Expect: "foo" * type Keys = ReadonlyKeys
      
       ; * /
      
export type ReadonlyKeys<T extends object> = {
  [P inkeyof T]-? : IfEquals< { [Qin P]: T[P] },
    { -readonly [Q in P]: T[P] },
    never,
    P
  >;
}[keyof T];

Copy the code

4.NonUndefined

To get rid of undefined

/ * * *@example* // Expect: "string | null" * SymmetricDifference<string | null | undefined>; * /
export type NonUndefined<A> = A extends undefined ? never : A;
Copy the code

5. FunctionKeys

Gets the combined types of all the function names in the object, both optional and required

/ * * *@example* type MixedProps = {name: string; setName: (name: string) => void; someKeys? : string; someFn? : (... args: any) => any; }; * * // Expect: "setName | someFn" * type Keys = FunctionKeys<MixedProps>; * /
export type FunctionKeys<T extends object> = {
  [K inkeyof T]-? : NonUndefined<T[K]>extends Function ? K : never;
}[keyof T];

Copy the code
  1. T extends objectIt is required that the generic T must be assigned to object in order to iterate over the property names of T
  2. [K in keyof T] iterates over all public property names of T and assigns the property names to K
  3. -?Each field of T is mandatory, so that TS does not automatically assign undefined to the field
  4. If a function is an optional propertyT[K] = ()=>{}|undefinedT[K] extends Function does not existNonUndefinedGet rid of undefined

6.Pick

Select the desired attributes from the generic T

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

interface Person{
  name:string.id:number.age:number
}

type NewPerson = Pick<Person,"name"|"age">//{name:string,age:number}
Copy the code

7. RequiredKeys

Gets the combined type of all required attribute names in the object

/ * * *@example* type Props = { req: number; reqUndef: number | undefined; opt? : string; optUndef? : number | undefined; }; * * // Expect: "req" | "reqUndef" * type Keys = RequiredKeys<Props>; * /
export type RequiredKeys<T> = {
  [K inkeyof T]-? : {}extends Pick<T, K> ? never : K;
}[keyof T];
Copy the code
  1. {} extends {id:number} ? true : falseReturns thefalse, because ID is a mandatory attribute, and{}Object does not exist
  2. {} extends {id? :number} ? true : falseReturns thetrueBecause ID is an optional attribute, null objects can also be assigned
  3. {} extends Pick<T, K> ? never : K, return never if T[K] is an optional attribute, K otherwise
  4. -?Make optional properties mandatory, or else,K? :neverThere’s one in front of K?T sub s will turn it into t sub SK? :never|undefined[keyof T] = undefined-? 

8.PickByValue

Find from all the attributes in the generic T an attribute whose attribute type can be assigned to ValueType

/ * * *@example* type Props = { req: number; reqUndef: number | undefined; opt? : string; }; * * // Expect: { req: number } * type Props = PickByValue<Props, number>; * // Expect: { req: number; reqUndef: number | undefined; } * type Props = PickByValue<Props, number | undefined>; * /
export type PickByValue<T, ValueType> = Pick<
  T,
  { [Key inkeyof T]-? : T[Key]extends ValueType ? Key : never }[keyof T]
>;
Copy the code
  1. The second argument to Pick is a union type, so you need to[keyof T]Get all the property types of the generic T
  2. T[Key] extends ValueType ? Key : neverThe attribute type of the generic T returns Key if it can be assigned to ValueType, or never otherwise
  3. (1) Attribute type (2)[key in keyof T]:Keyknown{ [Key in keyof T]-? : T[Key] extends ValueType ? Key : never }[keyof T]I can get all the property names of T

9. SetDifference

Return never if A can assign to B, and return A otherwise

/* * SetDifference (same as Exclude) * @desc Set difference of given union types `A` and `B` * @example * // Expect: "1" * SetDifference<'1' | '2' | '3', '2' | '3' | '4'>; * * // Expect: string | number * SetDifference
      
        void), Function>; * /
      
export type SetDifference<A, B> = A extends B ? never : A;
Copy the code

10.Omit

Find the properties of the generic T other than K

/* * @example * type Props = { name: string; age: number; visible: boolean }; * * // Expect: { name: string; visible: boolean; } * type Props = Omit
      
       ; * /
      ,>
export type Omit<T, K extends keyof any> = Pick<T, SetDifference<keyof T, K>>;
Copy the code
  1. keyof TReturns the union type of the property name of T
  2. SetDifference<keyof T,K>Find the union type C from T that does not have the attribute name in K
  3. Pick<T,C>Find an attribute from T whose attribute name is in C

11.Intersection

Get the properties common to both generics

/* @example * type Props = { name: string; age: number; visible: boolean }; * type DefaultProps = { age: number }; * * // Expect: { age: number; } * type DuplicateProps = Intersection
      
       ; * /
      ,>
export type Intersection<T extends object, U extends object> = Pick<
  T,
  Extract<keyof T, keyof U> & Extract<keyof U, keyof T>
>;
Copy the code
  1. type Extract<T, U> = T extends U ? T : never; 
  2. Extract<keyof T, keyof U> & Extract<keyof U, keyof T>I can get the intersection of the properties of T and U

12. Diff

Find a property in T that does not exist on U

/* @example * type Props = { name: string; age: number; visible: boolean }; * type DefaultProps = { age: number }; * * // Expect: { name: string; visible: boolean; } * type DiffProps = Diff
      
       ; * /
      ,>
export type Diff<T extends object, U extends object> = Pick<
  T,
  SetDifference<keyof T, keyof U>
>;
Copy the code
  1. SetDifference<keyof T,keyof U>Find the union type from T for attributes that do not exist in U



13.DeepPartial

Recursively make all attributes optional

export type DeepPartial<T> = T extends Function
  ? T
  : T extends Array<infer U>
  ? _DeepPartialArray<U>
  : T extends object
  ? _DeepPartialObject<T>
  : T | undefined;
/ * *@private * /
// tslint:disable-next-line:class-name
export interface _DeepPartialArray<T> extends Array<DeepPartial<T>> {}
/ * *@private * /
export type _DeepPartialObject<T> = { [P inkeyof T]? : DeepPartial<T[P]> };Copy the code
  1. T extends FunctionCheck whether T can be assigned to Function, if so return T,
  2. T extends Array<infer u>T can be assigned to Array because we don’t know what type of Array it isinfer UAs a type variable, when T can be assigned to Array, we need to recurse each item in Array,Array<DeepPartial<T>> 
  3. T extends ObjectTo determine whether T can be assigned to Object, if so, we need to recursively traverse every property in Object,[P in keyof T]Iterate over the property name of T,DeepPartial<T[P]>Pass in the property type of TDeepPartialrecursively

conclusion

If there are mistakes and omissions, please see the officers correct