Skip to content

数据类型

1.原始类型

1.1 概念

JavaScript 语言(注意,不是 TypeScript)将值分成 8 种类型:null、undefined、boolean、number、string、symbol、bigint。

TypeScript 继承了 JavaScript 的类型设计,以上 8 种类型可以看作 TypeScript 的基本类型。

注意,上面所有类型的名称都是小写字母,首字母大写的NumberStringBoolean等在 JavaScript 语言中都是内置对象,而不是类型名称。

另外,undefined 和 null 既可以作为值,也可以作为类型,取决于在哪里使用它们。

这 8 种基本类型是 TypeScript 类型系统的基础,复杂类型由它们组合而成。

1.2 定义语法

ts
修饰符 变量名: 数据类型 =

1.3 boolean

boolean类型只包含truefalse两个布尔值。

ts
const x: boolean = true;
const y: boolean = false;

1.4 string

string类型包含所有字符串。

typescript
const x: string = "hello";
const y: string = `${x} world`;

上面示例中,普通字符串和模板字符串都属于 string 类型。

1.5 number

number类型包含所有整数和浮点数。

typescript
const x: number = 123;
const y: number = 3.14;
const z: number = 0xffff;

上面示例中,整数、浮点数和非十进制数都属于 number 类型。

1.6 bigint

bigint 类型包含所有的大整数。

typescript
const x: bigint = 123n;
const y: bigint = 0xffffn;

上面示例中,变量xy就属于 bigint 类型。

TIP

bigint 与 number 类型不兼容。

typescript
const x: bigint = 123; // 报错
const y: bigint = 3.14; // 报错

上面示例中,bigint类型赋值为整数和小数,都会报错。

注意,bigint 类型是 ES2020 标准引入的。如果使用这个类型,TypeScript 编译的目标 JavaScript 版本不能低于 ES2020(即编译参数target不低于es2020)。

1.7 symbol

symbol 类型包含所有的 Symbol 值。

typescript
const x: symbol = Symbol();

上面示例中,Symbol()函数的返回值就是 symbol 类型。

1.8 undefined,null

undefined 和 null 是两种独立类型,它们各自都只有一个值。

undefined 类型只包含一个值undefined,表示未定义(即还未给出定义,以后可能会有定义)。

typescript
let x: undefined = undefined;

上面示例中,变量x就属于 undefined 类型。两个undefined里面,第一个是类型,第二个是值。

null 类型也只包含一个值null,表示为空(即此处没有值)。

typescript
const x: null = null;

上面示例中,变量x就属于 null 类型。

TIP

注意,如果没有声明类型的变量,被赋值为undefinednull,它们的类型会被推断为any

typescript
let a = undefined; // any
const b = undefined; // any

let c = null; // any
const d = null; // any

如果希望避免这种情况,则需要打开编译选项strictNullChecks

typescript
// 打开编译设置 strictNullChecks
let a = undefined; // undefined
const b = undefined; // undefined

let c = null; // null
const d = null; // null

上面示例中,打开编译设置strictNullChecks以后,赋值为undefined的变量会被推断为undefined类型,赋值为null的变量会被推断为null类型。

2.对象类型

2.1 Object

ts
const a: { test1: number; test2: string } = { test1: 11, test2: 'abcafsd' };

2.2 Array

声明元素是基本类型的数组

js
const arr1: number[] = [11, 22]

const arr2: string[] = ['hello', 'world']

const arr3: boolean[] = [true, false]

声明联合类型数组,元素的类型可以是多个,多个类型用|运算符合并

js
const arr1: (number | string)[] = [11, 'hello']

const arr2: (number | string | boolean)[] = [11, 'hello', true]

声明包含指定对象类型的数组

js
const arr1: {a:number,b:number}[] = [{a:1,b:2},{a:1,b:2}]

声明二维数组

js
const arr1: [number, number, number][] = [[1, 2, 3], [4, 5, 6]]

const arr2: { a: number; b: number }[][] = [
	[
		{ a: 1, b: 2 },
		{ a: 3, b: 4 },
	],
]

2.3 RegExp

ts
const reg: RegExp = /\d/

2.4 Date

ts
const b: Date = new Date()

2.5 Number

js
const num: Number = new Number(123)

const num1: Number = 456

2.6 String

ts
const str: String = new String(11)

const str1: String = "123456"

2.7 Function

语法:

ts
function 函数名(形参1: 数据类型1, 形参2: 数据类型2): 返回值类型 {
	return
}

参数不确定传不传,在形参后面添加?

ts
function 函数名(age?:number):number {
	return age
}

例子

ts
// 形参、返回值
function fn1(name:string,age:number):number {
	return age
}

const a:number = fn1('张三', 18)}

// 函数可选参数
function fn2(name:string,age:number, sex?:string):number {
	return age
}

const b:number = fn2('张三', 18)


// 箭头函数
const fn3:(name:stirng,age:number)=>number = function() {
	return 666
}

const data7 = (num1: number, num2: number = 66):string => {
    return '我就不用形参'
}
data7(11,222) 
data7(11)

3.新增类型

ts 新增数据类型:

  • string |number | ...:联合类型
  • any:任意类型
  • unknown:不详的/未知类型
  • type:类型别名自定义类型
  • tuple:元组
  • enum:枚举
  • void:无类型
  • never:永不存在的值的类型 。通常是抛出异常或者永远不会有返回值的类型

3.1 tuple

元组(tuple)是 TypeScript 特有的数据类型,JavaScript 没有单独区分这种类型。它表示成员类型可以自由设置的数组,即数组的各个成员的类型可以不同。

ts
const data:[number,number,string]=[1,2,'3']

注意

元组类型的写法,与数组有一个重大差异。数组的成员类型写在方括号外面(number[]),元组的成员类型是写在方括号里面([number])。

TypeScript 的区分方法是,成员类型写在方括号里面的就是元组,写在外面的就是数组。

typescript

let a: [number] = [1];

元组成员的类型可以添加问号后缀(?),表示该成员是可选的。

ts
let a: [number, number?] = [1];

注意,问号只能用于元组的尾部成员,也就是说,所有可选成员必须在必选成员之后。

ts
type myTuple = [number, number, number?, string?];

元组也可以是只读的,不允许修改,有两种写法。

typescript
// 写法一
type t = readonly [number, string];

// 写法二
type t = Readonly<[number, string]>;

3.2 enum

Enum用来是Ts新增数据类型枚举,常用来表示一些常量

3.2.1 枚举定义

最基本的枚举定义如下

ts
enum Week {
	Monday,
	Tuesday,
	Wednesday,
	Thursday,
	Friday,
	Saturday,
	Sunday,
}

默认情况下,枚举的值从0开始,依次递增,但我们可以给每个元素一个自定义的值

ts
enum Week {
	Monday = 1,
	Tuesday = 2,
	Wednesday = 3,
	Thursday = 4,
	Friday = 5,
	Saturday = 6,
	Sunday = 7,
}

也可以给中文

ts
enum Week {
	Monday = "周一",
	Tuesday = "周二",
	Wednesday = "周三",
	Thursday = "周四",
	Friday = "周五",
	Saturday = "周六",
	Sunday = "周末",
}

3.2.2 枚举访问

枚举的访问类似于对象,都是键值对

ts
enum Week {
	Monday,
	Tuesday,
	Wednesday,
	Thursday,
	Friday,
	Saturday,
	Sunday,
}

console.log(Week.Monday); 		// 0
console.log(Week.Tuesday); 		// 1
console.log(Week.Wednesday); 	// 2
console.log(Week.Thursday); 	// 3
console.log(Week.Friday); 		// 4
console.log(Week.Saturday); 	// 5
console.log(Week.Sunday); 		// 6

// 也可以通过[]访问

console.log(Week['Monday']) 		// 0
console.log(Week['Tuesday']) 		// 1
console.log(Week['Wednesday']) 		// 2
console.log(Week['Thursday']) 		// 3
console.log(Week['Friday']) 		// 4
console.log(Week['Saturday']) 		// 5
console.log(Week['Sunday']) 		// 6

3.2.3 Enum字符串

Enum 成员的值除了设为数值,还可以设为字符串。也就是说,Enum 也可以用作一组相关字符串的集合。

除了数值和字符串,Enum 成员不允许使用其他值(比如 Symbol 值)。

ts
enum Time {
	Morning = "早上",
	Afternoon = "下午",
	Evening = "晚上",
}

console.log(Time.Morning)			// 早上
console.log(Time.Afternoon)			// 下午
console.log(Time.Evening)			// 晚上

Enum 成员可以是字符串和数值混合赋值。

typescript
enum Enum {
  One = "One",
  Two = "Two",
  Three = 3,
  Four = 4,
}

注意

字符串枚举的所有成员值,都必须显式设置。如果没有设置,成员值默认为数值,且位置必须在字符串成员之前。

typescript
enum Foo {
  A, // 0
  B = "hello",
  C, // 报错
}

3.2.4 反向映射

数值 Enum 存在反向映射,即可以通过成员值获得成员名。

当我们把枚举打印出来,发现枚举的结构是双向的

ts
enum Time {
	Morning,
	Afternoon,
	Evening,
}

console.log(Time)

// 打印结果如下
{
  '0': 'Morning',
  '1': 'Afternoon',
  '2': 'Evening',
  Morning: 0,
  Afternoon: 1,
  Evening: 2
}

所以也可以反向访问,即可以通过成员值获得成员名。

ts
enum Time {
	Morning,
	Afternoon,
	Evening,
}

console.log(Time[0]) // Morning
console.log(Time[1]) // Afternoon
console.log(Time[2]) // Evening

这是因为 TypeScript 会将上面的 Enum 结构,编译成下面的 JavaScript 代码。

javascript
var Time;
(function (Time) {
  Time[(Time["Morning"] = 0)] = "Morning";
  Time[(Time["Afternoon"] = 1)] = "Afternoon";
  Time[(Time["Evening"] = 2)] = "Evening";
})(Time || (Time = {}));

上面代码中,实际进行了两组赋值,以第一个成员为例。

javascript
Time[(Time["Morning"] = 0)] = "Morning";

上面代码有两个赋值运算符(=),实际上等同于下面的代码。

javascript
Time["Morning"] = 0;
Time[0] = "Morning";

WARNING

这种情况只发生在数值 Enum,对于字符串 Enum,不存在反向映射。这是因为字符串 Enum 编译后只有一组赋值。

ts
enum MyEnum {
  A = "a",
  B = "b",
}

// 编译后
var MyEnum;
(function (MyEnum) {
  MyEnum["A"] = "a";
  MyEnum["B"] = "b";
})(MyEnum || (MyEnum = {}));

可以通过打印结果看到

ts
enum Time {
	Morning = "早上",
	Afternoon = "下午",
	Evening = "晚上",
}

console.log(Time)   // { Morning: '早上', Afternoon: '下午', Evening: '晚上' }

console.log(Time.Morning)	 // 早上
console.log(Time["早上"])		// 错误

3.2.5 Enum作类型

Enum 结构本身也是一种类型。

ts
enum Time {
	Morning = '早上',
	Afternoon = '下午',
	Evening = '晚上',
}

const m: Time = Time.Morning
const a: Time = Time.Afternoon
const e: Time = Time.Evening

console.log(m) // 早上
console.log(a) // 下午
console.log(e) // 晚上

定义的变量除了可以设置成Enum类型,也可以根据元素的值设置基本类型

ts
enum Time {
	Morning = 0,
	Afternoon = '下午',
	Evening = '晚上',
}

const m: Number = Time.Morning
const m1: Time = Time.Morning

const a: String = Time.Afternoon
const a1: Time = Time.Afternoon

TIP

Enum 结构的特别之处在于,它既是一种类型,也是一个值。绝大多数 TypeScript 语法都是类型语法,编译后会全部去除,但是 Enum 结构是一个值,编译后会变成 JavaScript 对象,留在代码中。

ts
// 编译前
enum Color {
  Red, // 0
  Green, // 1
  Blue, // 2
}

// 编译后
let Color = {
  Red: 0,
  Green: 1,
  Blue: 2,
};

3.2.6 keyof运算符

keyof 运算符可以取出 Enum 结构的所有成员名,作为联合类型返回。

ts
enum MyEnum {
  A = "a",
  B = "b",
}

// 'A'|'B'
type Foo = keyof typeof MyEnum;

3.3 any

any 类型表示没有任何限制,该类型的变量可以赋予任意类型的值

ts
let x: any;

x = 1; // 正确
x = "foo"; // 正确
x = true; // 正确

变量类型一旦设为any,TypeScript 实际上会关闭这个变量的类型检查。即使有明显的类型错误,只要句法正确,都不会报错。

ts
let x: any = "hello";

x(1); // 不报错
x.foo = 100; // 不报错

由于这个原因,应该尽量避免使用any类型,否则就失去了使用 TypeScript 的意义。

TIP

实际开发中,any类型主要适用以下两个场合。

  • 出于特殊原因,需要关闭某些变量的类型检查,就可以把该变量的类型设为any
  • 为了适配以前老的 JavaScript 项目,让代码快速迁移到 TypeScript,可以把变量类型设为any。有些年代很久的大型 JavaScript 项目,尤其是别人的代码,很难为每一行适配正确的类型,这时你为那些类型复杂的变量加上any,TypeScript 编译时就不会报错。

3.3.1 类型推断

对于开发者没有指定类型、TypeScript 必须自己推断类型的那些变量,如果无法推断出类型,TypeScript 就会认为该变量的类型是any

ts
function add(x, y) {
  return x + y;
}

add(1, [1, 2, 3]); // 不报错

这显然是很糟糕的情况,所以对于那些类型不明显的变量,一定要显式声明类型,防止被推断为any

3.3.2 污染问题

any类型除了关闭类型检查,还有一个很大的问题,就是它会“污染”其他变量。它可以赋值给其他任何类型的变量(因为没有类型检查),导致其他变量出错。

typescript

let x: any = "hello";
let y: number;

y = x; // 不报错

y * 123; // 不报错
y.toFixed(); // 不报错

3.4 unknown

为了解决any类型“污染”其他变量的问题,TypeScript 3.0 引入了unknown类型。它与any含义相同,表示类型不确定,可能是任意类型,但是它的使用有一些限制,不像any那样自由,可以视为严格版的any

unknownany的相似之处,在于所有类型的值都可以分配给unknown类型。

ts
let x: unknown;

x = true; // 正确
x = 42; // 正确
x = "Hello World"; // 正确

unknown类型跟any类型的不同之处在于,它不能直接使用。主要有以下几个限制。

  • 首先,unknown类型的变量,不能直接赋值给其他类型的变量(除了any类型和unknown类型)。

    ts
    let v: unknown = 123;
    
    let v1: boolean = v; // 报错
    let v2: number = v; // 报错
  • 其次,不能直接调用unknown类型变量的方法和属性。

    ts
    let v1: unknown = { foo: 123 };
    v1.foo; // 报错
    
    let v2: unknown = "hello";
    v2.trim(); // 报错
    
    let v3: unknown = (n = 0) => n + 1;
    v3(); // 报错
  • 再次,unknown类型变量能够进行的运算是有限的,只能进行比较运算(运算符=====!=!==||&&?)、取反运算(运算符!)、typeof运算符和instanceof运算符这几种,其他运算都会报错。

    ts
    let a: unknown = 1;
    
    a + 1; // 报错
    a === 1; // 正确

想要使用unknown类型的变量,需要将类型缩小,所谓“类型缩小”,就是缩小unknown变量的类型范围,确保不会出错。

  • 可以使用typeof

    ts
    let a: unknown = 1;
    
    if (typeof a === "number") {
      let r = a + 10; // 正确
    }
  • 也可以使用typeof

    ts
    let n: unknown = 111
    
    let num:number =n as number

3.5 never

为了保持与集合论的对应关系,以及类型运算的完整性,TypeScript 还引入了“空类型”的概念,即该类型为空,不包含任何值。

由于不存在任何属于“空类型”的值,所以该类型被称为never,即不可能有这样的值。

ts
let x: never;

上面示例中,变量x的类型是never,就不可能赋给它任何值,否则都会报错。

never类型的使用场景,主要是在一些类型运算之中,保证类型运算的完整性,详见后面章节。另外,不可能返回值的函数,返回值的类型就可以写成never,详见《函数》一章。

如果一个变量可能有多种类型(即联合类型),通常需要使用分支处理每一种类型。这时,处理所有可能的类型之后,剩余的情况就属于never类型。

ts
function fn(x: string | number) {
  if (typeof x === "string") {
    // ...
  } else if (typeof x === "number") {
    // ...
  } else {
    x; // never 类型
  }
}

上面示例中,参数变量x可能是字符串,也可能是数值,判断了这两种情况后,剩下的最后那个else分支里面,x就是never类型了。

never类型的一个重要特点是,可以赋值给任意其他类型。

typescript
function f(): never {
  throw new Error("Error");
}

let v1: number = f(); // 不报错
let v2: string = f(); // 不报错
let v3: boolean = f(); // 不报错

上面示例中,函数f()会抛错,所以返回值类型可以写成never,即不可能返回任何值。各种其他类型的变量都可以赋值为f()的运行结果(never类型)。

为什么never类型可以赋值给任意其他类型呢?这也跟集合论有关,空集是任何集合的子集。TypeScript 就相应规定,任何类型都包含了never类型。因此,never类型是任何其他类型所共有的,TypeScript 把这种情况称为“底层类型”(bottom type)。

总之,TypeScript 有两个“顶层类型”(anyunknown),但是“底层类型”只有never唯一一个。

4.联合类型

联合类型(Union Types)表示取值可以为多种类型中的一种。联合类型使用 | 分隔每个类型。

ts
let myFavoriteNumber: string | number;

myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

MIT Licensed