类型系统
TIP
本章只介绍开发中常用的类型,更多类型请查看 Typescript官方文档。
基础类型
string
字符串类型。
let str: string = 'Hello, TypeScript!';
number
数字类型。
let num: number = 123;
boolean
布尔类型。
let isDone: boolean = false;
symbol
这是ECMAScript 2015引入的是一种新原始数据类型,它是由 Symbol
构造函数创建的,不可变,并且是唯一的。
let sym: symbol = Symbol('hi');
为了表示某个具体 Symbol
值的类型,可以使用symbol
的一个子类型unique symbol
:
declare const sym1: unique symbol;
// sym2 只能是一个常量引用!
let sym2: unique symbol = Symbol(); // Error: A 'unique symbol' type must be 'const' or 'readonly'
// 如果要引用 unique symbol,必须使用 typeof 运算符
let sym3: typeof sym1 = sym1;
// 也可以通过 readonly 静态属性声明 unique symbol
class C {
static readonly StaticSymbol: unique symbol = Symbol();
}
bigint
大整数类型,用于表示大于 2^53 - 1
的整数。
let x: bigint = 1234n;
注意,bigint
与 number
类型不兼容。
tuple
元组类型,一个已知元素数量和类型的数组,各元素的类型不必相同。
let tuple: [string, number] = ['hello', 123];
Array
数组类型,内部元素(成员)数量不限,但必须是同一类型。 要指定数组类型,最常见的方法是在元素类型后面加上 []
。如number[]
表示由数字组成的数组,另一种方法是使用 泛型 (后文将详细介绍)。
let arr1: number[] = [1, 2, 3];
let arr2: Array<number> = [1, 2, 3];
注意,成员类型写在方括号([number]
)里面的就是元组,写在外面的就是数组(number[]
),这两者很容易混淆。
当数组没有声明类型时,TypeScript 会进行类型推断:
let arr1 = [1]; // number[]
let arr2 = ['hello']; // string[]
如果变量初始值为一个空数组,数组类型被推断为是any[]
,这不难理解,当后续对这个数组赋值等操作时,TypeScript 会自动为其进行更新类型推断:
let arr = []; // any[]
arr.push(1); // number[]
arr.push('hello'); // (number | string)[]
要注意的是,数组的类型更新只适用于初始值为空数组的情况。
let arr = [1]; // number[]
// arr的类型已经被推断为number[],所以不能再添加其他类型的值
arr.push('hello'); // Error: Argument of type 'string' is not assignable to parameter of type 'number'.
Function
表示任意函数类型。
let func1: Function = () => {};
let func2: Function = function() {};
关于函数类型的详细介绍,可以查看 函数 章节。
object
object
表示非原始类型,即所有对象、数组、函数。
let obj: object = { name: 'Alice' };
let arr: object = [1, 2, 3];
let func: object = () => {};
any
可以赋值为任意类型的值。
let anyValue: any = 123;
anyValue = 'hello';
很多初学者为了方便,会在项目中大量使用any
类型,因此 Typescript 常常被戏称为 “anyScript” 🤓,变量类型一旦设为any
,TypeScript 实际上会关闭这个变量的类型检查,应该尽量避免使用any
类型。
如果你想避免 any
的滥用,Typescript提供了一个编译选项 noImplicitAny
,当开启这个选项时,TypeScript会在没有明确指定类型的情况下报错。
undefined
和 null
undefined
和 null
是两种特殊的独立类型,它们既是值,又是类型。
let u: undefined = undefined;
let n: null = null;
默认情况下,null
和 undefined
是所有类型的子类型,这意味着你可以将 null
和 undefined
赋值给任何类型的变量。
let num: number = null;
let str: string = undefined;
Object
所有可以转成对象的值,都是 Object
类型。因此除了 null
和 undefined
之外,其他值都是 Object
类型的实例。
let str: Object = 'hello';
let num: Object = 123;
let obj: Object = { name: 'Alice' };
let arr: Object = [1, 2, 3];
let func: Object = () => {};
object
也可以表示成 {}
,这样写会简洁一点,两者是一样的。
但是,Object
的类型过于松散,它可以接受几乎任何类型的值,因此在实际开发中,应该尽量避免使用 Object
类型。
WARNING
Object
和 object
是完全不同的两种类型,书写时要注意大小写,切勿混淆。
void
void
表示没有任何类型,通常用于函数没有返回值时。
function log(message: string): void
{
console.log(message);
}
void
类型的变量只能赋值为 undefined
。
never
never
表示永远不会出现的值,通常用于抛出异常或无限循环。
function error(message: string): never
{
throw new Error(message);
}
unknown
unknown
类型是 TypeScript 3.0 新增的类型,它是所有类型的父类型,可以赋值给任何类型。
let unknownValue: unknown = 123;
unknownValue = 'hello';
unknown
类型的变量只能赋值给 any
类型,或者使用类型断言转换为其他类型。
let anyValue: any = unknownValue;
let str: string = unknownValue as string;
特殊类型
值类型
和JS不同的是,单个值在TS也是一种类型,称为“值类型”。
let msg: 'hello'; // 'hello' 是字符串字面量,也是一种类型
msg = 'hello'; // OK
msg = 'hi'; // Error: Type '"hi"' is not assignable to type '"hello"'.
值类型的值只能是它本身,不能是其他值。上述例子中,msg
的类型是 'hello'
,而不是字符串类型。
const
命令声明的变量(除对象外),如果代码里面没有注明类型,就会推断该变量是值类型。
const msg = 'hello'; // msg的类型推断为 'hello'
单独使用值类型的场景并不多见,实际开发中,往往将多个值结合,作为联合类型使用。
联合类型
联合类型表示一个值可以是几种类型之一。用 |
分隔每个类型。
let value: string | number = 'hello';
value = 123;
如上述所示,value
可以是 string
类型,也可以是 number
类型。
联合类型可以与值类型相结合,也是很常见的。
let direction: 'up' | 'down' | 'left' | 'right' = 'up';
由于联合类型的值是多个类型的并集,因此只能访问所有类型的共有属性和方法,如果要访问特定类型的属性和方法,需要使用 类型缩小:
function print(value: string | number) {
if (typeof value === 'string') {
console.log(value.toUpperCase());
} else {
console.log(value.toFixed(2));
}
}
交叉类型
交叉类型表示多种类型组成的新类型。用 &
分隔每个类型。
let obj: { x: number } & { y: string } = { x: 1, y: 'hello' };
obj
的类型是 { x: number } & { y: string }
,即必须同时满足 x
和 y
属性,缺一不可。
类型别名
类型别名可以为类型起一个新名字,方便复用。
type Name = string;
type Age = number;
type Person = {
name: Name;
age: Age;
};
let person: Person = { name: 'Alice', age: 18 };
类型别名可以是任何类型,包括联合类型、交叉类型等,相当于一个变量,因此不能重复定义,并且有块级作用域限制。
获取类型
获取变量的类型,可以使用 typeof
关键字。
let num = 123;
type NumType = typeof num; // number
这是一个很有用的方法,可以用来获取变量的类型,然后在其他地方复用。