Open vaakian opened 2 years ago
keyof
获取一个interface / type的key列表的「联合类型」
in
遍历「联合类型」
in
例子
type Keys = "firstName" | "lastName"
type Person = {
[key in Keys]: string
}
// 会得到以下类型
type Person = {
firstName: string;
lastName: string;
}
keyof
例子
type Person = {
name: string
age: number
sex: "男" | "女"
}
// Person2是一个映射类型,与Person的效果一样
type Person2 = {
[P in keyof Person]: Person[P]
}
keyof Person
拿到一个关于Person
的key
列表联合类型 "name" | "age" | "sex"
然后通过in
遍历这个列表,又通过Person[P]
拿到键P
在Person
中对应的类型。
可以看到,上面创建的Person2与Person的效果并无差别。所以有什么用呢? 以下是可以用到的例子:
// 让类型T的所有属性只读
type MyReadonly<T> = {
readonly [prop in keyof T]: T[prop]
}
// 让类型T的所有属性可选
type MyOptional<T> = {
[prop in keyof T]?: T[prop];
}
let p1: MyReadonly<Person> = {
name: 'Xwj',
age: 22,
sex: '男'
}
p1.age = 23 // Cannot assign to 'age' because it is a read-only property
let p2: MyOptional<Person> = {
name: 'Xwj',
age: 22
} // 不赋值sex也不会报错
⚠️注意: 通过in来"映射"的类型不能再重新定义其他属性。会报以下错误。
A mapped type may not declare properties or methods 映射类型不能声明属性或方法
type Person2 = { [P in keyof Person]: Person[P]; personId: string // A mapped type may not declare properties or methods }
但是可以通用
&
创建一个交叉类型
实现类似需求。type Person2 = { [P in keyof Person]: Person[P]; } & { personId: string }
交叉类型通过&
运算符来创建,尝试将两边的类型进行合并操作。
例子:
// 给Person类型新增了personId属性
type Person3 = Person & { personId: string }
而遇到冲突/重复键时,该属性就会被识别为never
类型,也就是不可能出现的值类型。
下面例子的Person3的age属性为never类型,因为不可能出现即是number
又是string
类型的值。
type Person3 = Person & { age: string }
// 更简单的例子
type age = number & string
联合类型通过 |
运算符创建,像编程语言的或
运算,返回的类型语意为多个类型的其中之一。
通常用来约束变量值在某一个列表中。
// 表明age可能是string也可能是age
type age = number | string
// 实际用途
type Rank = "优秀" | "良好" | "及格" | "不及格"
let rank: Rank = "棒棒的" // Type '"棒棒的"' is not assignable to type 'Rank'
never
表示没有类型,
而void
表示空类型,早期版本void
类型包含null
和undefined
,而目前最新版只能是undefined
。
在以下两种情况会用到never
类型。
// 抛出错误
function throwError(errorMsg: string): never {
throw new Error(errorMsg);
}
// 无限循环
function keepProcessing(): never {
while (true) {
console.log('infinite loop!')
}
}
在函数能够正常结束,但没有返回值或者返回undefined
的情况下,可以用void
类型。
function func1(): void {
return undefined
}
function func2(): void {
// no return
}
function func3(): void {
return void 0
}
冷知识:
如果需要一个undefined
值,最好不要直接用undefined
,因为undefined
是一个全局变量,可能会被更改导致程序预期不一致。
void 0、void 任何值,它们的返回值都为undefined
,而void 0
最短,一般用它来创建一个undefined
值。
在TypeScript中有两种文件格式,.ts
格式包含类型定义和可执行代码,而.d.ts
文件仅包含类型定义,仅用于类型检查(Type Checking),不会被编译成js
文件。
在同一个项目中定义的类型名称不可重复,因为在默认情况下它们都属于同一个全局模块。解决办法是在子文件内使用export
语句,那么会它创建一个本地作用域。
// some code
https://ts.xcatliu.com/basics/declaration-files.html#declare-module
类型推断 & 自动取类型
属性扩展
⚠️注意:通过
&
运算符创建的属性如果重复/冲突时,后面的类型不会覆盖前面,而会变为never
类型。而extends
继承方式创建扩展类型时,会直接报错。解决办法是有的,通过
Omit
操作符先去掉重复的属性,再重新定义。更复杂的方式,age & name不可修改