Skip to content

类型系统

TIP

本章只介绍开发中常用的类型,更多类型请查看 Typescript官方文档

基础类型

string

字符串类型。

typescript
let str: string = 'Hello, TypeScript!';

number

数字类型。

typescript
let num: number = 123;

boolean

布尔类型。

typescript
let isDone: boolean = false;

symbol

这是ECMAScript 2015引入的是一种新原始数据类型,它是由 Symbol 构造函数创建的,不可变,并且是唯一的。

typescript
let sym: symbol = Symbol('hi');

为了表示某个具体 Symbol 值的类型,可以使用symbol的一个子类型unique symbol

typescript
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 的整数。

typescript
let x: bigint = 1234n;

注意,bigintnumber 类型不兼容。

tuple

元组类型,一个已知元素数量和类型的数组,各元素的类型不必相同。

typescript
let tuple: [string, number] = ['hello', 123];

Array

数组类型,内部元素(成员)数量不限,但必须是同一类型。 要指定数组类型,最常见的方法是在元素类型后面加上 []。如number[]表示由数字组成的数组,另一种方法是使用 泛型 (后文将详细介绍)。

typescript
let arr1: number[] = [1, 2, 3];
let arr2: Array<number> = [1, 2, 3];

注意,成员类型写在方括号([number])里面的就是元组,写在外面的就是数组(number[]),这两者很容易混淆。

当数组没有声明类型时,TypeScript 会进行类型推断:

typescript
let arr1 = [1]; // number[]
let arr2 = ['hello']; // string[]

如果变量初始值为一个空数组,数组类型被推断为是any[],这不难理解,当后续对这个数组赋值等操作时,TypeScript 会自动为其进行更新类型推断:

typescript
let arr = []; // any[] 
arr.push(1); // number[]
arr.push('hello'); // (number | string)[]

要注意的是,数组的类型更新只适用于初始值为空数组的情况。

typescript
let arr = [1]; // number[]
// arr的类型已经被推断为number[],所以不能再添加其他类型的值
arr.push('hello'); // Error: Argument of type 'string' is not assignable to parameter of type 'number'.

Function

表示任意函数类型。

typescript
let func1: Function = () => {};
let func2: Function = function() {};

关于函数类型的详细介绍,可以查看 函数 章节。

object

object 表示非原始类型,即所有对象、数组、函数。

typescript
let obj: object = { name: 'Alice' };
let arr: object = [1, 2, 3];
let func: object = () => {};

any

可以赋值为任意类型的值。

typescript
let anyValue: any = 123;
anyValue = 'hello';

很多初学者为了方便,会在项目中大量使用any类型,因此 Typescript 常常被戏称为 “anyScript” 🤓,变量类型一旦设为any,TypeScript 实际上会关闭这个变量的类型检查,应该尽量避免使用any类型。

如果你想避免 any 的滥用,Typescript提供了一个编译选项 noImplicitAny,当开启这个选项时,TypeScript会在没有明确指定类型的情况下报错。

undefinednull

undefinednull 是两种特殊的独立类型,它们既是值,又是类型。

typescript
let u: undefined = undefined;
let n: null = null;

默认情况下,nullundefined 是所有类型的子类型,这意味着你可以将 nullundefined 赋值给任何类型的变量。

typescript
let num: number = null;
let str: string = undefined;

Object

所有可以转成对象的值,都是 Object 类型。因此除了 nullundefined 之外,其他值都是 Object 类型的实例。

typescript
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

Objectobject 是完全不同的两种类型,书写时要注意大小写,切勿混淆。

void

void 表示没有任何类型,通常用于函数没有返回值时。

typescript
function log(message: string): void
{
    console.log(message);
}

void 类型的变量只能赋值为 undefined

never

never 表示永远不会出现的值,通常用于抛出异常或无限循环。

typescript
function error(message: string): never
{
    throw new Error(message);
}

unknown

unknown 类型是 TypeScript 3.0 新增的类型,它是所有类型的父类型,可以赋值给任何类型。

typescript
let unknownValue: unknown = 123;
unknownValue = 'hello';

unknown 类型的变量只能赋值给 any 类型,或者使用类型断言转换为其他类型。

typescript
let anyValue: any = unknownValue;
let str: string = unknownValue as string;

特殊类型

值类型

和JS不同的是,单个值在TS也是一种类型,称为“值类型”。

typescript
let msg: 'hello'; // 'hello' 是字符串字面量,也是一种类型
msg = 'hello'; // OK
msg = 'hi'; // Error: Type '"hi"' is not assignable to type '"hello"'.

值类型的值只能是它本身,不能是其他值。上述例子中,msg 的类型是 'hello',而不是字符串类型。

const 命令声明的变量(除对象外),如果代码里面没有注明类型,就会推断该变量是值类型。

typescript
const msg = 'hello'; // msg的类型推断为 'hello'

单独使用值类型的场景并不多见,实际开发中,往往将多个值结合,作为联合类型使用。

联合类型

联合类型表示一个值可以是几种类型之一。用 | 分隔每个类型。

typescript
let value: string | number = 'hello';
value = 123;

如上述所示,value 可以是 string 类型,也可以是 number 类型。

联合类型可以与值类型相结合,也是很常见的。

typescript
let direction: 'up' | 'down' | 'left' | 'right' = 'up';

由于联合类型的值是多个类型的并集,因此只能访问所有类型的共有属性和方法,如果要访问特定类型的属性和方法,需要使用 类型缩小

typescript
function print(value: string | number) {
  if (typeof value === 'string') {
    console.log(value.toUpperCase());
  } else {
    console.log(value.toFixed(2));
  }
}

交叉类型

交叉类型表示多种类型组成的新类型。用 & 分隔每个类型。

typescript
let obj: { x: number } & { y: string } = { x: 1, y: 'hello' };

obj 的类型是 { x: number } & { y: string },即必须同时满足 xy 属性,缺一不可。

类型别名

类型别名可以为类型起一个新名字,方便复用。

typescript
type Name = string;
type Age = number;
type Person = {
  name: Name;
  age: Age;
};
let person: Person = { name: 'Alice', age: 18 };

类型别名可以是任何类型,包括联合类型、交叉类型等,相当于一个变量,因此不能重复定义,并且有块级作用域限制。

获取类型

获取变量的类型,可以使用 typeof 关键字。

typescript
let num = 123;
type NumType = typeof num; // number

这是一个很有用的方法,可以用来获取变量的类型,然后在其他地方复用。