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
keyof
You can get all the keys in the object, Object.keys()
similar , if we want to get all the keys in a certain interface, keyof
we 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
in
It can be used to cooperate keyof
. We know from the above keyof
that 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 keyof
traversing , this K can also be accessed by other places. Have you started to feel keyof
the effect of ?
2.2 Use in alone
in
If 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: number
If length: string
, TS will also report an error, because length returns a number
type instead ofstring
4. Index signature
索引签名
It means [K : keyType]: valuetype
that 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
Record
It is 索引签名
very similar is used to define keyType: valueType
the 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
infer
Literally, 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>
infer
Not 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
typeof
It 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!