当前位置:网站首页>Pre knowledge reserve of TS type gymnastics to become an excellent TS gymnastics master

Pre knowledge reserve of TS type gymnastics to become an excellent TS gymnastics master

2022-07-06 07:29:00 Jioho_

TS Type gymnastics pre knowledge reserve

If you are learning TS, But like me, I just stay in defining types , Definition interface/type At the level of , Don't miss this gymnastics type exercise ! type-challenges

When writing this article, I only finished the medium-level questions of gymnastics type ( also 2-3 Tao is not completely solved )

Simply build a question making environment

I like to make a copy of one question , So I clone A copy of type-challenges. And then in type-challenges Created a new folder (my-type-challenges), It's dedicated to TS Artistic Gymnastics

Every time you want to do a question, you just need to remember the number , For example, the first question 13-helloword . Just input

npm run copy 13

The detailed script is in Jioho/my-type-challenges. After writing the script , You can write code happily

Basic grammar of gymnastics introduction

  • The following contents are purely personal opinions , Please point out any mistakes or misunderstandings

Although I say TS Turing Complete ( If a computing system can calculate every Turing computable function , Then this system is Turing complete )

however TS There is not so much grammar , such as if,switch,return And so on. .

TS The most used are Ternary operator , Judge equal , Array , recursive Some skills will be introduced one by one later

TS Built in advanced types ( Built in Gymnastics )

The first step to getting started is to look at the documentation ( Although I don't like watching , Look not to understand ), But you still need to have a basic understanding utility-types

The current built-in method is as follows :

Partial<Type>Required<Type>Readonly<Type>Record<Keys, Type>
Pick<Type, Keys>Omit<Type, Keys>Exclude<UnionType, ExcludedMembers>Extract<Type, Union>
NonNullable<Type>Parameters<Type>ConstructorParameters<Type>ReturnType<Type>
InstanceType<Type>ThisParameterType<Type>OmitThisParameter<Type>ThisType<Type>
Uppercase<StringType>Lowercase<StringType>Capitalize<StringType>Uncapitalize<StringType>

For example, take one that may be used more in the follow-up :

Pick Method

Official website demo:

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, "title" | "completed">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};

Pick The function of is from an object , Select the required fields , For instance from TODO Only take out title and completed

If there is no type of gymnastics ,TodoPreview You have to define an additional type

interface TodoPreview {
  title:string;
  completed: boolean;
}

I encountered this situation in a previous hand training project , Obviously, they are all fields on the same object , In order to adapt to different scenes , Just define a lot of subordinate fields , The key is if you want to change a type , We have to change all of them .. If there is Pick It's all done , Whatever you want pick what

Want to see Pick The implementation is also simple , Pick up any one of them type, Hold down ctrl+ Click on Pick You can see that Pick Realization

The code implementation is as follows :

/**
 * From T, pick a set of properties whose keys are in the union K
 */
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

But the first time I saw this code , extends , keyof , in What are these things ? This is the meaning of this article . To look down

Common judgment extends keyword

extends Translation means extension

stay TS Acted as if and stay XXX Within the scope of One of the functions of .extends It is usually followed by the ternary operator ( There are exceptions. )

Take up a Son

type mytype1 = 1 extends string ? true : false // false

type mytype2 = '1' extends string ? true : false // true

type mytype2_1 = string extends '1' ? true : false // false

type mytype3 = mytype1 extends any ? 1 : 2 // 1

type mytype4 = [90] extends unknown[] ? true : false // true

type mytype5 = [90] extends string[] ? true : false // false

A few simple examples are given above , A simple explanation :

  • 1 Whether to belong to string type obtain false because 1 It's the number type
  • ‘1’ Belong to is string Type of The ternary operator is judged as true
  • string type Belong to ‘1’ Must be false Of ,string Type range ratio ’1’ Bigger , be not in Belong to The category of the
  • mytype1 Belong to any The type is right , because any Include everything ~
  • [90] Is a numerical array , Belong to a unknown Array of unknown type , This is also true , Because unknown types also contain numeric types
  • and [90] It doesn't belong to string[] The category of the

extends There are also times when the ternary operator is not connected

For example, write a limit string Type of push Method

type StrPush<T extends string[], U extends string> = [...T, U]
type myarr = StrPush<[1, 2, 3], 4>

For example, in this case , It's a direct error . Because I haven't entered StrPush In the judgment of ,T Generics have been constraint by strinig Type ,U Also bound to string type

extends summary

  • extends stay TS Of The body of the function When I was in, I played Judgment category One of the functions of
  • In some special places ( For example, when receiving generics , When asserting variable types during function operations ) It plays a role Constraint type The role of

The key of the loop object keyof and in

Just know keyof and in after ,Pick All the keywords of are explained , The first simple question in gymnastics practice MyPick And we're done

  • It's still up there demo Code
/**
 * From T, pick a set of properties whose keys are in the union K
 */
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, "title" | "completed">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};

extends The above mentioned , Belong to category judgment / Constraint type , In the generic definition <> It's obviously for constraint types

keyof It can be understood as Put one object All in key extracted .

  • Direct use keyof Extract an object to see the effect :
type TodoKeys = keyof Todo
//type TodoKeys =  'title' | 'description' | 'completed'

type testkeys = keyof {[k:number]: any; name:string}
// type testkeys = number | "name"

//  Special examples 
type Mapish = { [k: string]: boolean; name:string };
type M = keyof Mapish;
// type M = string | number

In the above examples , The first is the best understood , Extract all of key

In the second case ,k As unknown content , extract number As key The scope of the , add “name” So we get number | "name"

A special example is also an example of the official website , Why is there a number type ?

Original words : Note that in this example, M is string | number — this is because JavaScript object keys are always coerced to a string, so obj[0] is always the same as obj[“0”].
translate : Please note that , In this example ,M string | number — This is because JavaScript Object keys are always cast to strings , therefore obj[0] Always with obj[“0”] identical

combination demo ,K extends keyof T Which means ,K The value range of parameters can only be Todo Key of ('title' | 'description' | 'completed'), Limit to string , And this 3 One of the keys / Multiple , Only less , Not much

If it's the following

type Mapish = { [k: string]: boolean; name:string };
// K extends keyof Mapish;

K The scope of is string | number type ( Because there is no specific key name , So only No restrictions on specific key names , Only the types are restricted

Use it like this | Put together ( Or type ) The standard point is called unio Federated data type , Want to cycle these data , You can use in keyword


go back to demo Explanation

  • Pick in Generic T What's coming in is Todo type
  • K extends keyof T : K Limit to Todo Key value of ( What's coming in is “title” | “completed” Meet the requirements , Because it can only be less but not more )
  • P in k It can be understood as loop
  • P Will be assigned to title
  • Then assign a value completed
  • T[P] Meaning and JS Of [] Same value , obtain T[‘title’] => string and T[‘completed’] => completed
  • {} It will wrap the result of circulation
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

type TodoPreview = Pick<Todo, "title" | "completed">;
// TodoPreview = {title:string,completed:boolean}

Pick The implementation of is completed .[P in K] This cycle and we JS The conventional way of writing is very different , It needs to be digested , Everything else should be easy to understand


  • Push in , complete Readonly
    • as everyone knows readonly Just add readonly Parameters can be

The implementation is as follows :keyof Written in brackets , This is allowed , No matter where these keywords are written, as long as they meet the specifications OK, such as MyReadonly2

type MyReadonly<T> = {
  readonly [P in keyof T]: T[P]
}

//  This is to paint the snake to the foot , because P It must belong to keyof T Of , It's from keyof T Out of the circulation 
//  But prove keyof It's not just  <>  Can be used in 
type MyReadonly2<T> = {
  readonly [P in keyof T]: P extends keyof T ?  T[P] : never
}

Small expansion : 'name' extends keyof T ? true : false Can also judge T Is there any in this generic object name This attribute

keyof and in Summary

  • keyof To get all of an object Key name , When the key name is a type , Will be upgraded to the corresponding type
  • in Is for circulation unio Union type data , Most of them are used to regenerate an object in a loop

JS turn TS – typeof keyword

typeof As from js The world turns into ts The content of the world .

Suppose a scenario , We use one obj Objects are stored as data maps , For example, use a map, Store the status code and return the corresponding msg:

const statusMap = {
    
  200: ' Successful operation ',
  404: ' Content not found ',
  500: ' operation failed ',
  10001: ' Login failure '
}

var status1 = 200
console.log(statusMap[status1]) //  Successful operation 

var status2 = 10001
console.log(statusMap[status2]) //  Login failure 

Similar to the above scenario , At this time of the statusMap in [] The middle value must be 200,404,500,10001 To meet the requirements , stay TS Naturally, we have to agree status In this range

If not typeof , We can write something like this TS:

type Status = 200 | 404 | 500 | 10001

var status1: Status = 200
console.log(statusMap[status1]) //  Successful operation 

var status2: Status = 10001
console.log(statusMap[status2]) //  Login failure 

var status3: Status = 301 //  Report red  (Type '301' is not assignable to type 'Status')

At this time, suppose we add several key values , that TS You have to change it in synchronization ( It's too troublesome ), use typeof

const statusMap = {
    
  200: ' Successful operation ',
  404: ' Content not found ',
  500: ' operation failed ',
  10001: ' Login failure '
}

type MyStatusMap = typeof statusMap
//  Now  MyStatus  The value of will become 
// type MyStatus = {
    
// 200: string;
// 404: string;
// 500: string;
// 10001: string;
// }

//  Then connect. keyof keyword , Extract the key name of the object 
type MyStatus = keyof MyStatusMap
// type MyStatus = 200 | 404 | 500 | 10001

//  above 2 One step can be abbreviated to one step 
type MyStatus2 = keyof typeof statusMap
// type MyStatus2 = 200 | 404 | 500 | 10001

This only requires us to change JS The content of ,TS Will automatically get the new object . It's not enough to return the object , Because what we want to constrain above is the incoming key value ( Want to get the key value , Just above learning keyof keyword ), You can dynamically obtain all the key values that meet the specifications !

typeof Summary

typeof It is a system that can dynamically put JS Object to TS Key words of .

But there are also limited scenarios , That is, the premise of transformation is this part JS yes Fixed content . Just like an object mapping in the example , That is the fixed content , And then let TS To deduce

And the packaged code cannot exist TS Of , If you want to implement the back-end interface to dynamically return the content in use typeof , It can't be done

Loops of arrays and strings Inference type infer

infer There are many application scenarios

In a nutshell infer Just one. placeholder Tools for , I stand in this position , As for the content of this position infer Not care , It can be left to the later program to judge

Use simple questions 00014-easy-first Let's explain . Achieve one First Tool type

Usually get the first element , What we think of is T[0] Of course. TS This grammar is workable

There can be one item in the test case

Equal<First<[]>, never> //  Use  T[0]  This will report an error , because  First<[]>  The return is  undefined

That is, when this element is declared as undefined when , stay TS Most of them use never Instead of

The correct answer is as follows :

type First<T extends any[]> = T extends [infer F,...infer Rest] ? F : never

Simply put

  • infer Must be in TS Used in function operation ( Defining generic <> in Out of commission , and extends Can )
  • infer Can cooperate with … Carry out operations
  • T extends [infer F,...infer Rest]
    • The expression means T extends [F,…Rest The remaining values ].T There must be an attribute F Array of ,...Rest It's the rest , not essential
    • ...infer Rest Is to remove F Other elements are being collected into an array ( This is ES6 The knowledge of the )
    • infer F It means , I take F In this array placeholder , The contents of the first item of the array , Is being F Account for the
  • Return to the topic , We want to take the first item , So direct return F type
  • If T It's an empty array , that extends That step is judged to fail , The natural return is never, Meet the requirements of test cases .

infer Other wonderful uses of

infer It can also traverse strings

Like a The need to cut strings into arrays :

type Split<T extends string, U extends unknown[] = []> =
  T extends `${infer F}${infer Rest}` ? Split<Rest, [...U, F]> : U


type testSplit = Split<'123456'>

// type testSplit = ["1", "2", "3", "4", "5", "6"]

among ${infer F}${infer Rest} It means ,F Occupy the first character ,Rest Occupy the remaining characters , Because it doesn't exist in the string ... The concept of , therefore Rest Basically, it occupies the remaining characters

A test example like this , Altogether 3 Placeholders ,

type testInfer<T extends string> = T extends `${
      infer F}${
      infer S}${
      infer R}` ? [F, S, R] : T

type testInfer1 = testInfer<'123456'>
//  According to the characteristics of placeholders , front F and S To occupy separately 2 Characters , Give the rest R Occupied 
// type testInfer1 = ["1", "2", "3456"]

//  Make a little change , stay S Add a... After the placeholder 5
type testInfer2<T extends string> = T extends `${
      infer F}${
      infer S}5${
      infer R}` ? [F, S, R] : T
type testInfer3 = testInfer<'123456'>
// F  Occupy the first character  = 1
// S  occupy 2-4, Because in R There was one before 5, therefore S Represents the beginning of the second character to 5 All characters of 
//  that R It's from 5 Start , To the end , So the results are as follows :
// type testInfer1 = ["1", "234", "6"]

There are many more exercises that will be used later infer , So first understand infer placeholder The characteristics of

infer Summary

infer Equivalent to a keyword occupying a position , Copy the occupied position to the corresponding operation variable .

For arrays or other types , Can also be used … Sum up all the positions to form an array

For strings, this does not exist … To extend the operator , As long as the front occupies a position , The remaining characters will be replaced by the second placeholder

Array usage

An array is TS A very important characteristic of gymnastics , Because in TS There is no concept of addition and subtraction in gymnastics , The operation of addition and subtraction is indispensable in practical operation , Including getting length and so on .

So the array also acts as Counter The role of . There is another very useful trick about array counters , So useful that I think I can write a separate article to analyze it in detail , Now let's briefly introduce the functions of arrays

Let's start with a simple question , Learn the basic properties and usage of arrays 00018-easy-tuple-length

The title gives 2 An array , It was used as const. We need to find this 2 The length of an array

const tesla = ['tesla', 'model 3', 'model X', 'model Y'] as const
const spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT'] as const

type teslaLength = Length<typeof tesla>
type spaceXLength = Length<typeof spaceX>

//  answer :
type Length<T extends readonly any[]> = T['length']

Is it simple ?! T['length'] Finished ~

So a very important feature of arrays is , He has length attribute .

Want to use length Premise of attribute : T extends any[]
T The range of types , It must be an array ,any[] perhaps unknown[]. Will do , It's an array anyway , There is length attribute .


Based on this problem of finding length , Extend the , seek 2 The combined length of arrays

type MergeArray<T extends readonly any[],U extends readonly any[]> = [...T,...U]['length']

type arrLength = MergeArray<typeof tesla, typeof spaceX> // 9

Let's have a little interesting topic to experience the role of arrays Questions with medium difficulty 05153-medium-indexof

Achieve one indexOf( There is another point to pay attention to in the original question, which is what we need to pay attention to when judging the type , I will explain later in this article , Now let's look at the simplest implementation ):

Since we need to calculate the index position , Naturally, it involves a Counter The problem of , Look at the answers below

type numberIndex = IndexOf<[1, 2, 3], 1> //  The answer you need is  0
type numberIndex2 = IndexOf<[1, 2, 3], 99> //  The answer you need is  -1
type numberIndex3 = IndexOf<[1, 2, 3], 1> //  The answer you need is  0

//  The initial template given by the title ( Missing counter )
// type IndexOf<T extends unknown[],U> = any

//  For these missing parameters , We can completely fill in a parameter by ourselves , And supplement the default value 
type IndexOf2<T extends unknown[], U, C extends 1[] = []> =         
  T extends [infer F, ...infer Rest] ?             
    (F extends U ? C['length'] : IndexOf<Rest, U, [...C, 1]>) : -1

The explanation part :

  • Because the template given by the title lacks a counter , We can add one C Variable , And the default assignment is [].
    • As long as there is a default value , It is not required , If it is not required, the test case will not report an error
  • T extends and infer Some of them have been explained , If T Not satisfied with at least one F When , explain T The array is empty , When it is empty, it means that the result can be obtained
    • T It's empty , The data has not been matched , Press indexOf The method of should be to return -1
    • T If it meets at least one F Variable requirements , Judge F With the incoming U Data comparison , If the same, return the length of the current counter ( That is, the index to be calculated ) C['length']
    • If it doesn't fit , take Rest The array continues to loop ( Recursively call IndexOf), meanwhile When calling recursively C It becomes [...C,1] The array length is incremented 1
  • [...C,1] The role of , Is to keep the contents of the original array , Adding a 1, Make the original array length + 1 Counter The effect is achieved

Through a simple expansion operator , Add a 1 Make the length of the array constantly change . Reach the counter / The effect of addition

For example, in the first place. 4182 In the title of , There needs to be a Fibonacci sequence . The characteristic of Fibonacci sequence is n = (n-1)+(n-2). How to realize this addition ? In terms of code, it's […(N-1),…(N-2)].( It doesn't matter if you don't understand , There will be a detailed article later )

Array summary

  • As long as the corresponding type extends [] Words , You can use [‘length’] Property to get the length
  • Arrays in gymnastics not only act as arrays , It also acts as Counter and Add implementation The role of , Usage is ever-changing
  • The accumulation of arrays depends on Recursive method The implementation of the , In each recursive process, a new length is passed into the new method ( But recursion is easy to cause memory overflow , such as 02257-medium-minusone This question ). Need a more advanced skill to deal with

as keyword

When explaining arrays above, I saw as const Appearance , By the way as

stay TS In the use ,as It's just one. Assertion

Let's imagine a scenario like this , There is a variable todo , Set up in order to Todo type , There are corresponding properties

Then... Of a function side effect , Led to my todo Become a string type ( The actual code should avoid this side effect function )

but It just happened , And you can't change , By this time todo Should be string type .todo You also need to call a method , Need to stirng Type of function todoFn(str:string){} . At this time, it is directly transmitted todo I'm sure there will be an error , The type does not conform to

The solution is todoFn(todo as string). It will be easier to understand by looking at the code screenshot below

After I concluded that this type is xxx Type can be used as keyword ( Of course, it is not recommended to use ), Try to use TS Type derivation of


stay TS When defining the type of ,as It has other meanings

Like this one

const teslaConst = ['tesla', 'model 3', 'model X', 'model Y'] as const
// const teslaConst: readonly ["tesla", "model 3", "model X", "model Y"]

//  use var Variables and const The derivation is the same , But it will leave code hidden trouble , Not recommended 
var teslaConst2 = ['tesla', 'model 3', 'model X', 'model Y'] as const
// var teslaConst: readonly ["tesla", "model 3", "model X", "model Y"]

const tesla = ['tesla', 'model 3', 'model X', 'model Y']
// const tesla: string[]

The difference is obvious ,as const Will take out all the values , And become readonly. because const It's really a read-only tag .

however TS Don't eat js Variable type , So I have to pass as const To tell TS, I assert that this is a const Array , The elements inside will not be changed , You can traverse the values here

useless as const I only think it's a string[] Array of . This is a big difference

Last

TS The pre knowledge reserve of type gymnastics is probably introduced extends,infer,typeof,keyof and in, Use of arrays ,as keyword

After understanding the functions of these keywords , complete TS A moderately difficult topic in gymnastics practice A cinch !( At least finish 80% No problem with your title ), The rest 20% Still need to learn more TS Gymnastics skills

This feeling is like if you want to untie a One variable quadratic equation Before , You have to learn the usage and rules of addition, subtraction, multiplication and division . The above introduction is the basic rules of addition, subtraction, multiplication and division , Later, we need to learn more skillful skills to complete more complex TS Artistic Gymnastics

If you are interested, you can go to the homepage to see about TS Other articles on Gymnastics

The above knowledge points are also for me as a TS Some notes summarized by Xiaobai after fumbling for medium-sized topics
Please point out any mistakes or misunderstandings

原网站

版权声明
本文为[Jioho_]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/187/202207060723417803.html