TypeScript & Explain in detail the meaning of in, keyof, extends, index signature, Record, typeof (updated from time to time)

foreword

I have heard of TypeScript very early. I tried it myself and found it quite simple. It is to declare a type for data to improve readability and maintainability. However, TS was not popular at that time, and the project had no chance to use it, so it kept falling. With TS With the continuous popularity, many projects have begun to introduce TS. After seeing some TS codes on github recently, I started to complain in my heart: Is
it really necessary to decorate JS so fancy? I can't even recognize what language this is. Are you sure this is to improve the readability of the JS language instead of adding confusion? If there are students like me, please raise your hand!

Afterwards, I calmed down and thought about it. If you come here, you will be at ease. Instead of complaining, it is better to calm down and review TS. During this period, I also extracted some keywords that need to be mastered. Now I will read some TS codes in my heart. With the bottom line, it can be said that we understand the meaning of these keywords and can understand most TypeScript codes.

Of course, it’s not enough just to understand it, we have to learn to use it, but our current goal is to understand it first~

Tip: This article assumes that you already know some basic TS syntax. If you are new to TS, it is recommended to read the TypeScript daily basic syntax I wrote earlier.

text

1. keyof

keyofYou can get all the keys in the object, Object.keys()similar , if we want to get all the keys in a certain interface, keyofwe can use it, let's look at an example:

interface User {
    
    
	name: string;
	age: number;
	birthday: string;
}

// 下面等同于 type keysType = 'name' | 'age' | 'birthday'
type keysType = keyof User

const key:keysType = 'name'

It looks like a bit of help, it is nothing more than copying the key, don't worry, its real function is to match other keywords, we will know when we look down.

2. in

2.1 in keyof

inIt can be used to cooperate keyof. We know from the above keyofthat it is to get all the keys. What if we want to perform additional processing on each key? The keyword
can be used here , let's look at an example:in

interface Properties {
    
    
	name: string;
	age: number;
	ate: () => void;
	likeColors: string[];
}

// 将 Properties 的 key: valueType 都复制过来了
type CopyProperties = {
    
    
  [K in keyof Properties]: Properties[K]
}
// 等同于
// type CopyProperties = Properties

From the above example, we know that while in keyoftraversing , this K can also be accessed by other places. Have you started to feel keyofthe effect of ?

2.2 Use in alone

inIf used alone, it is similar to in in JS

// 表示 foo 定义的 key 必须包含 a 或 b 或 c
type Foo = {
    
    
	[K in 'a' | 'b' | 'c']: number
}
const obj: Foo = {
    
    
  a: 100,
  b: 200,
  c: 300,
  d: 400  // 报错
}

3. extends

3.1 interface extends

Literally interface extends, it seems to be called interface inheritance, you can call it that, but I personally prefer to call it 接口合并, let's look at an example.

interface Action {
    
    
	bark: () => void
}
interface People  extends Action{
    
    
	name: string;
	age: number;
}

// 上面的 People 等同于下面 
interface Peoople {
    
    
	name: string;
	age: number;
	bark: () => void
}

3.2 <T extends U>

Knock on the blackboard: Please forget the inheritance concept we have learned before, because the extends that we will talk about next is not called inheritance at all.

Literally understanding <T extends U > seems to be called generic inheritance? No! This is called 泛型约束, what is a generic constraint? Here I summarize it:
泛型传递进来的类型必须满足 U 里的所有属性和类型
let's look at an example:

interface U {
    
    
	name: string;
	age: number;
}
type Foo<T extends U> = {
    
    
	colors: string[];
	sex: boolean;
	user: T
}

interface User {
    
    
  name: string;
  age: number;
}
// User 满足 U,所以 TS 不会报错
const firstPerson: Foo<User> = {
    
    
  colors: ['Blue'],
  sex: true,
  user: {
    
    
    name: 'Jack',
    age: 20
  }
}

interface Water {
    
    
	color: 'Transparent';
	age: 10900000000000
}
// Water 这个类型不满足 U,所以 TS 会报错
const secondPerson: Foo<Water> = {
    
    }

It can be concluded from the examples that the defined generics are restricted and cannot be passed casually. Let's give another example to deepen the impression:

interface Properties {
    
    
	length: number;
}

function countStrLength<T extends Properties>(arg: T) {
    
    
	console.log('字符串的长度是' + arg.length)
}
// 正确,字符串里有 length 属性而且是 number 类型
countStrLength('Hello,world') 
// 传递的 number 类型没有 length 属性, TS 报错!
countStrLength(1000)

Note : The generic constraint not only constrains the key, but also limits the valueType. length: numberIf length: string, TS will also report an error, because length returns a numbertype instead ofstring

4. Index signature

索引签名It means [K : keyType]: valuetypethat K can be named arbitrarily, and the popular point is to define the unknown quantity keyType: valueType. Let's look at the example to understand

interface User {
    
    
	name: string;
	age: 20;
}

/*
假设 User 只有 name/age 两个属性,但后续可能会新增其它属性,且数量是未知的,
这种情况下我们就可以用索引签名来代表未知的 keyType: valueType
稍作修改就会变成下面这样
*/

interface User {
    
    
	name: string;
	age: number;
	[k: string]: any
}
// 接下来新增的的属性 TS 都不会报错了。
const firstPeople: User =  {
    
    
	name: 'Jack',
	age: 20,
	sex: 0,
	colors: ['Blue', 'Yellow']
}


// 如果你想让 [k: string]: any 变成可选状态,只需在后面加个问号 `?` 即可

interface User {
    
    
	name: string;
	age: number;
	[k: string]?: any
}
const secondPeople: User = {
    
    
	name: "Tony',
	age: 24,
}

Tip: The type of the key generally only has three attributes: string/number/symbol. For me, string can meet most of the needs. Symbol is only used in a few cases. As for number, it is very rare.

4. Record

RecordIt is 索引签名very similar is used to define keyType: valueTypethe form of unknown quantity. Let's look at an example

type R = Record<string, {
    
    
	name: string;
	age: number;
}>

const Players: R = {
    
    
	one: {
    
    
		name: 'Jack',
		age: 20
	},
	two: {
    
    
		name: 'Tony',
		age: 21
	},
	// ...
}

4.1 What is the difference between Record and Index Signature?

Use whichever you feel meets your needs.

5. infer

inferLiterally, it is relatively abstract, but it is relatively simple to use. It is a 提取类型function,
and there are prerequisites for using it. It must be combined with andextends . Let's look at an example:?

interface Animal {
    
    
  name: string;
  age: number;
  action: () => number
}

type GetType<T> = T extends {
    
     action: () => infer R} ? R : T

/**
因为 Animal 里的 action 存在,所以提取 action => 里的返回值,并用 R 表示。
以下等同于 type getAnimalType = number
*/
type getAnimalType = GetType<Animal> 

/**
因为不存在 action 所以返回 T
以下等同于 type getAnimalType = boolean
*/
type getAnimalType = GetType<boolean>

inferNot only can types be extracted, it also supports unions

interface Animal {
    
    
	name: string;
	age: number;
}
type GetType<T> = T extends {
    
     name: infer R; age: infer R; } ? R : T

/**
因为 Animal 里的 name 和 age 都存在,所以提取 name/age 的类型并用 R 表示。
以下等同于 type getAnimalType = string | age
*/
type getAnimalType = GetType<Animal> 

6. typeof

typeofIt can be used to copy the type declaration in a variable
. First of all, we know that when declaring a variable, TS will automatically deduce the type without specifying the type, such as

const UserName = 'Jack' 

// TS 自动推导后
const UserName:string = 'Jack' // 如果你有用 vscode 编辑器可以用鼠标移动到这个变量就可以看到效果了

Knowing this, let's take a look at the following example to understand

const UserName = 'Jack'
const AnotherUser: typeof UserName = 'Tony'

Let's give another example: If there is a parameter in a function that needs to copy the declared type in an object, we can do this:

const dog = {
    
    
	title: 'Dog	can speaking'
	properties: {
    
    
		name: 'Duolo',
		type: 'Big',
		color: 'Yellow',
	},
	actions: {
    
    
		say() {
    
     console.log('Hi, I am a human, not a dog at all.')},
	}
	
}

// 现在这个 properties 默认推导成 properties: any 类型,
function getPropertyByType(properties) {
    
    
	if (feature.name === 'Duolo') {
    
    
		dog.actions.say()
	}
}

// 如果我们要求 properties 传递进来的必须包含 dog.properties 类型,
// 这时就可以用到 typeof 
function getPropertyByType(properties: typeof dog['properties']) {
    
    
	if (feature.name === 'Duolo') {
    
    
		dog.actions.say()
	}
}

After learning these keywords, you need to continue to apply them to deepen your impression,
otherwise you will forget them after a while and have to come back to learn them again.

Please point out any mistakes, it's over!

Guess you like

Origin blog.csdn.net/cookcyq__/article/details/125087375