Contact us:Assistant of Youdao Technical team: ydtech01 /email:[email protected]

As front-end projects continue to grow in size, collaborative development has become a standard for front-end development, and TypeScript is being used in more and more projects. This change is not a blind pursuit of technology, but a business-driven technological advancement. TypeScript greatly improves code quality by providing strong typing support for native JavaScript, reducing the potential for hidden bugs that can occur when different module interfaces call each other in collaborative scenarios. This series of articles is based on my experience of learning and using TypeScript tool types in daily development. This series is divided into three parts and aims to achieve the following goals:

  • Understand the implementation mechanism for each tool type from a source code perspective
  • Walk through a simple example or two to understand the basic usage of each tool type
  • At the same time, deepen your understanding of TypeScript
  • The final realization can be drawn from the actual work

Partial: Optional

1.1 Source code Interpretation

The Partial source looks like this:

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

There are four points to note here:

  • <T>: This is the target type, the type we are dealing with. The type is uncertain, so it is represented by the generic T
  • [P in keyof T]: keyof T returns a type of all keys of type T. In can be in accordance with js for.. In traverses to understand, and keyof will be explained in more detail later
  • ?: Optional, all attributes of the return type are converted to optional types
  • Returns a new type that comes from T and has an attribute like TInheritance relationshipsIn section 2, yesRequired<Type>This will be verified in the description of the

The Partial

Type returns a new Type that has the same properties as the target Type T, but all of the properties are optional.

1.2 Actual Usage

Scenario Description: In actual business development, you often need to update a Partial or Partial data object. You can use Partial

.

interface DataModel {
  name: string
  age: number
  address: string
}

let store: DataModel = {
  name: ' '.age: 0.address: ' '
}

function updateStore (store: DataModel, payload: Partial
       ) :DataModel {
  return{... store, ... payload } } store = updateStore(store, {name: 'lpp'.age: 18
})
Copy the code

Try it yourself

1.3 supplement

Here’s an explanation for keyof, to understand it:

interface Person {
  name: string;
  age: number;
  location: string;
}

type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[];  // "length" | "push" | "pop" | "concat" | ...
type K3 = keyof { [x: string]: Person };  // string

const person: Person = {
  name: ' '.age: 0.location: ' '
}

type k11 = keyof typeof person; // "name" | "age" | "location"
Copy the code

Reference 1: Official documentation

What does “keyof Typeof” mean in TypeScript?

A: It is Required

2.1 Source code Interpretation

type Required<T> = {
    [P inkeyof T]-? : T[P]; };Copy the code

The purpose of this type is to make all attributes of type T optional.

The source code here uses a -? To label the attribute as a required attribute, so this -? Is it necessary? Because what we understand as an optional property is using? Well marked is optional if we put -? If removed, why can’t the effect of Required be achieved? Let’s start with a MyRequired, like this:

type MyRequired<T> = {
  [P inkeyof T]: T[P]; }; interface Props { a? : number; b? : string; }const obj: MyRequired<Props> = { 
  a: 5
};
Copy the code

Try it yourself

The above code is type-free. Why? Because if it were just [P in keyof T], the property in P would retain its own optionality in T. That is, if it was required before, it is still required in the new type, and it is the same if it is optional. It’s kind of like an “inheritance relationship.” So use -? To clear the optional implementation Required. Of course, +? B) Partial b) Partial D) Partial The + ‘of theta can be omitted.

2.2 Actual Usage

Required converts all attributes of type T passed in as Required. So the most common use is to do conversions like this, but if you just want to make certain attributes of type T mandatory and return them as a new type:

interface Props { a? : string b? : string c? : string }// Keep only b,c attributes and make them mandatory
type NewProps1 = Required<Pick<Props, 'b' | 'c'>>

// All attributes of Props need to be preserved, but b, c need to be filled in
type NewProps2 = Partial<Props> & Required<Pick<Props, 'b' | 'c'>>

const obj: NewProps2 = {
  b: '1'.c: '2'
}
Copy the code

Try it yourself

ReadOnly: ReadOnly

3.1 Source code Interpretation

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};
Copy the code

Set the property contained in type T to readonly and return a new type. Readonly, as the name implies, is read-only and cannot be modified after initialization. This type can be used in conjunction with javascript’s const keyword to reference the value of a type attribute as a constant.

One limitation of this type is that the child can only be set to read-only. If the child is also a reference type, this does not apply to the child. Is there any way to recursively set all references to be read-only?

Please leave comments and discuss

3.2 Actual Usage

interface Person {
    name: string
    age: number
}

const person: Readonly<Person> = {
    name: 'lpp'.age: 18
}

person.age = 20;    // Cannot be assigned to "age" because it is read-only. ts(2540)
Copy the code

Without readOnly, in javascript, assigning a const variable to a unique reference type, such as an object, allows you to change the value of the property, but not the reference stored in the variable. To make the value of an object’s property immutable, You can use object.freeze in javascript.

function freeze<Type> (obj: Type) :Readonly<Type>;
Copy the code

Record<Keys, Type>

The utility Type constructs a Type whose key Type is Keys and value Type is Type Type.

4.1 Source code Interpretation

type Record<K extends keyof any, T> = {
    [P in K]: T;
};
Copy the code

Here we see that the type definition for K is keyof any. Here keyof any string | number | symbol, as shown below:

type a = keyof any;
/ / equivalent to the
type a = string | number | symbol;
Copy the code

>> Actual usage

// Simply define key and value types
type Obj1 = Record<string, string>

// Generate new types based on other types
type FruitTypes = 'apple' | 'banana' | 'pear'

interface FruitInfo {
  name: FruitTypes
  price: number
}

type Fruits = Partial<Record<FruitTypes, FruitInfo>>

const fruits: Fruits = {
  apple: {
    name: 'apple'.price: 10}}Copy the code

Try it yourself

5, Pick<Type, Keys

From Type Type, select a set of attributes to form a new Type. This set of attributes is qualified by Keys, which are strings or combinations of strings.

5.1 Interpretation of source code

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

** K extends keyof**, which means K needs to be a subset of keyof T. The key of the returned type should be [P in K] and the value type should be T[P].

5.2 Actual Usage

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

// The child has no ID
type Toddler = Pick<Person, 'name' | 'age'>
Copy the code

Omit anything. Omit anything

Construct a Type that contains the attributes of Type Type except Keys. Keys is a string or union of strings.

6.1 Source Code Interpretation

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type Exclude<T, U> = T extends U ? never : T;
Copy the code

Since Omit relies on Exclude, the source code of the Exclude type is posted here. Exclude<T, U> is used to Exclude types from T that can be assigned to U.

I won’t tell you how to implement Exclude, just what it does.

Therefore, Omit <keyof T, K> can be regarded as a counter selection, and Omit the attributes of T that are not included in K, and then use Pick to implement Omit.

6.2 Actual Usage

interface Person {
  name: string
  age: number
  id: string
  work: string
  address: string
  girlFriend: number
}

// People who have no jobs
type PersonNoWork = Omit<Person, 'work'>

// A person with no address
type PersonNoAddress = Omit<Person, 'address'>

// No girlfriend
type PersonNoGirlFriend =Omit<Person, 'girlFriend'
Copy the code

7. Exercises

How to implement a tool type SelectRequired<T, K in Keyof T> to achieve the following:

interface Props { a? : string b? : string c? : stringd: string
}

type NewProps = SelectRequired<Props, 'b' | 'c'>;    // { a? : string, b: string, c: string, d: string }
Copy the code

The answer point is here

Eight, next time notice

Stay tuned for the following in the next installment of Playing with TypeScript tool Types (Middle) :

  • Mandatory: extends condition operator
  • Exclude<Type, ExcludeUnion>
  • Extract<Type, Union>
  • NonNullable
  • Mandatory: tuple type Tuple type
  • Parameters
  • ConstructorParameters
  • ReturnType
  • InstanceType