文章目录
1. 理解any、unknown、never和void
any
any 类型表示没有任何限制,该类型的变量可以赋予任意类型的值
变量类型一旦设为any,TypeScript 实际上会关闭这个变量的类型检查。即使有明显的类型错误,只要句法正确,都不会报错。
应该尽量避免使用any类型,否则就失去了使用 TypeScript 的意义
unknow
与any含义相同,表示类型不确定,可能是任意类型,但有一些限制:
- unknown类型的变量,不能直接赋值给其他类型的变量(除了any类型和unknown类型)
- 不能直接调用unknown类型变量的方法和属性
never
never类型表示的是那些永不存在的值的类型
function fn(x:string|number) {
if (typeof x === 'string') {
// ...
} else if (typeof x === 'number') {
// ...
} else {
x; // never 类型
}
}
never类型的一个重要特点是,可以赋值给任意其他类型
never类型是任何其他类型所共有的,TypeScript 把这种情况称为“底层类型”(bottom type)
void
某种程度上来说,void类型像是与any类型相反,它表示没有任何类型
声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null
当一个函数没有返回值时,你通常会见到其返回值类型是 void,而不是undefined类型。否则将报错
2. Object、object 和 {}
TypeScript 的对象类型也有大写Object和小写object两种
Object
除了undefined和null这两个值不能转为对象,其他任何值都可以赋值给Object类型
object
object类型代表 JavaScript 里面的狭义对象,即可以用字面量表示的对象
只包含对象、数组和函数,不包括原始类型的值
{}
空对象{}是Object类型的简写形式,所以使用Object时常常用空对象代替
无论是大写的Object类型,还是小写的object类型,都只包含 JavaScript 内置对象原生的属性和方法,用户自定义的属性和方法都不存在于这两个类型之中。
const o1:Object = { foo: 0 };
const o2:object = { foo: 0 };
o1.toString() // 正确
o1.foo // 报错
o2.toString() // 正确
o2.foo // 报错
3. type 命令、 typeof 运算符和 keyof 运算符
type
type命令用来定义一个类型的别名
- 别名不允许重名
- 别名的作用域是块级作用域
- 别名支持使用表达式
- type命令属于类型相关的代码,编译成 JavaScript 的时候,会被全部删除
typeof
TS 中 typeof 可以用来获取一个变量或对象的类型
当一个 interface 总有一个字面量初始值时,可以考虑如下写法以减少重复代码
keyof
keyof 用于得到一个对象(TS类型)的公共属性的联合类型
keyof any
的结果为 string | number | symbol
keyof (A & B) <=> keyof A | keyof B
keyof (A | B) <=> keyof A & keyof B
4. 数组与元组
数组
TypeScript 数组有根本特征:所有成员的类型必须相同
如下图,x 为 `string | number`,而非 `number`
TypeScript 允许使用方括号读取数组成员的类型
type Names = string[];
type Name = Names[0]; // string
只读数组
数组类型前面加上readonly关键字即可声明只读数组
只读数组不可删除、修改、新增数组成员
只读数组是数组的父类型
注意,readonly关键字不能与数组的泛型写法一起使用
实际上,TypeScript 提供了两个专门的泛型,用来生成只读数组的类型
const arr:readonly Array<number> = [0, 1]; // 报错
const a1:ReadonlyArray<number> = [0, 1];
const a2:Readonly<number[]> = [0, 1];
const断言可声明只读数组
const arr = [0, 1] as const;
arr[0] = [2]; // 报错
元祖
元组(tuple)是 TypeScript 特有的数据类型,表示成员类型可以自由设置的数组
- 元组必须明确声明每个成员的类型,否则 TypeScript 会把一个值自动推断为数组
- 元组成员的类型可以添加问号后缀(?),表示该成员是可选的(只可用于尾部成员)
- 可(任意位置)使用扩展运算符(…),可以表示不限成员数量的元组
- 元组可以通过方括号,读取成员类型
只读元组
与数组相同,只读元组是元组的父类型,只读元组不能替代元组
一旦扩展运算符使得元组的成员数量无法推断,TypeScript 内部就会把该元组当成数组处理
5. interface 与 type 的异同
interface 与 type 都可以表示对象类型,几乎所有的 interface 命令都可以改写为 type 命令。二者区别有如下几点:
- type能够表示非对象类型,而interface只能表示对象类型(包括数组、函数等)
- interface可以继承其他类型,type不支持继承(type定义的对象类型如果想要添加属性,只能使用&运算符,重新定义一个类型)
- 同名interface会自动合并,同名type则会报错
- interface不能包含属性映射(mapping),type可以
interface Point { x: number; y: number; } // 正确 type PointCopy1 = { [Key in keyof Point]: Point[Key]; }; // 报错 interface PointCopy2 { [Key in keyof Point]: Point[Key]; };
- this关键字只能用于interface
- type 可以扩展原始数据类型,interface 不行
- interface无法表达某些复杂类型(比如交叉类型和联合类型),但是type可以
综上所述,如果有复杂的类型运算,那么没有其他选择只能使用type;一般情况下,interface灵活性比较高,便于扩充类型或自动合并,建议优先使用