基本数据类型

状态: 初稿

介绍

我们的程序需要存储和处理数据, 而所有复杂数据结构都是由数字, 字符串, 结构, 布尔类型等基本单元组合而成的.
TypeScript 不仅包含几乎所有你期望在 JavaScript 见到的那些基本数据类型, 还新增了枚举类型帮助你写出更可读的程序. 本文将介绍它们.

布尔类型(Boolean)

布尔类型(Boolean), 你只能用它表示两个值——真(true), 假(false). 用关键字 boolean 来定义一个布尔类型的变量.

1
let isDone: boolean = false;

数字类型(Number)

数字类型(Number). 跟 JavaScript 一致, 所有 TypeScript 中的数字都是浮点数.
我们用 number 关键字来定义数字型变量.
从 ECMAScript 2015 开始, TypeScript 同时支持十进制, 十六进制, 二进制, 和八进制字面量.
以下都是对一个 number 变量的正确初始化.

1
2
3
4
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;

字符串(String)

无论是创建网页还是类服务端(servers alike)应用, 字符串(String, 或文本类型)都是另一种重要的数据类型.
我们用关键字 string 来定义一个字符串变量.
字符串字面量必须以成对的单引号 (‘) 或双引号 (“) 环绕.

1
2
let color: string = "blue";
color = 'red';

模板字符串(template strings)能够在源程序中跨越多行. 它们以反引号 (`) 环绕;
你还可以在模板字符串内嵌入表达式, 这些表达式形如: ${ expr }

1
2
3
4
5
let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${ fullName }.

I'll be ${ age + 1 } years old next month.`;

以上对 sentence 的定义等同于:

1
2
let sentence: string = "Hello, my name is " + fullName + ".\n\n" +
"I'll be " + (age + 1) + " years old next month.";

数组(Array)

数组(Array)存储相同类型的一组元素.
有两种描述数组类型的方式:
其一, 在元素类型后面加一对方括号([])将其扩展为存储该类型的数组类型:

1
let list: number[] = [1, 2, 3];

其二, 特化泛型数组类型 Array<elemType>.

译注: 参考泛型一章.

1
let list: Array<number> = [1, 2, 3];

元组(Tuple)

元组(Tuple)类似数组, 但有几个不同点:

  1. 数组的长度是可变的, 元组长度不可变;
  2. 数组中的元素类型一致, 元组的每个元素类型可以不同.

以下定义表示一个 string 值, 一个 number 值的二元组:

1
2
3
4
5
6
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ["hello", 10]; // OK
// Initialize it incorrectly
x = [10, "hello"]; // Error

你可以用下标访问元组中位于不同位置的元素, 该表达式值的类型是取得的元素类型:

1
2
console.log(x[0].substring(1)); // OK
console.log(x[1].substring(1)); // Error, 'number' does not have 'substring'

访问不存在的下标引发程序错误:

1
2
3
x[3] = "world"; // Error, Property '3' does not exist on type '[string, number]'.

console.log(x[5].toString()); // Error, Property '5' does not exist on type '[string, number]'.

枚举(Enum)

枚举类型(Enum)是一项对标准 JavaScript 很有用的补充.
我们知道, C# 等支持枚举的语言能够赋予一组数字好记的名字.

1
2
enum Color {Red, Green, Blue}
let c: Color = Color.Green;

枚举元素的值默认从 0 开始依次递增.
你也可以手动为一个元素指定值, 它后面元素的值继续在它的基础上递增.
下例, 我们让枚举 Color1 开始:

1
2
enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;

你还可以手动为所有枚举元素指定它代表的数值.

1
2
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;

一个方便的特性是反向查询 — 查询数值在一个枚举集合中对应的枚举成员名.
比如, 我们不知道数值 2 代表 Color 枚举集合中的哪个成员, 可借助反向查询语法:

1
2
3
4
enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];

console.log(colorName); // Displays 'Green' as its value is 2 above

Any

有时, 我们暂不能确定一个变量将要存储什么类型的值.
它的值可能来自动态内容(dynamic content), 比如用户, 第三方库.
针对这种情况, 我们把它的类型设为 any.
以表明不希望这个变量参与静态类型检查, 直接通过编译:

1
2
3
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

any 是一条用于沿用现有 JavaScript 的强大途径, 它允许你逐步引入和移除编译期类型检查.
有人认为它等同于其他语言的 Object.
然而, 这并不准确, 虽然你可以把任何变量赋值给一个 Object, 但你不能在这个引用上任意调用原变量的方法, 即使这些方法的确存在:

1
2
3
4
5
6
let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)

let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.

注: 如同在我们 Do’s and Don’ts 一章描述的, 避免像使用原始类型 object 那样使用 Object.

假如你仅了解一个类型的一部分 — 而不是所有, any 也能带来方便.
比如说, 你有一个元组 (译注: 原文是数组), 它的元素类型不尽相同.

1
2
3
let list: any[] = [1, true, "free"];

list[1] = 100;

Void

你可以按 any 的相反面来理解 void: 不存在任何类型.
通常, 你会看到这个关键字作为函数的返回值类型出现, 表明这个函数不返回值.

1
2
3
function warnUser(): void {
console.log("This is my warning message");
}

定义 void 类型的变量不见得特别有用, 因为你只能把 null 值(在没指定 --strictNullChecks 选项的前提下, 参见下节)或 undefined 值赋给它.

1
2
let unusable: void = undefined;
unusable = null; // OK if `--strictNullChecks` is not given

Null 和 Undefined

nullundefined 不仅都是特殊值, 它们对应的类型名也是 nullundefined.
void, 我们一般不单独定义 nullundefined 类型的变量.

1
2
3
// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;

通常, nullundefined 是其他所有类型的子类型.
这意味着你可以把 null 值或 undefined 值赋值给 number 等变量.

但是, 当 --strictNullChecks 选项打开后, nullundefined 就只能赋值给 any 和它们自身的类型了, 即 nullundefined(例外之一是 undefined 还可以赋值给 void).
该选项能够消除许多常见错误.
如果你确实希望随时把 string, null, 或 undefined 之一赋值给同一个变量, 你可以把这个变量定义为 string, nullundefined 的联合: string | null | undefined.

我们在以后的章节重点探讨联合数据类型.

注: 我们鼓励打开 --strictNullChecks, 但是在这本手册中, 我们假定它是关闭的.

Never

never 表示永远不会产生值.
例如: 如果一个函数表达式或箭头函数表达式始终抛出异常, 或从不返回, 它的返回值类型就是 never;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Function returning never must have unreachable end point
function error(message: string): never {
throw new Error(message);
}

// Inferred return type is never
function fail() {
return error("Something failed");
}

// Function returning never must have unreachable end point
function infiniteLoop(): never {
while (true) {
}
}

又如: 一个变量 x 属于类型护卫永不为真的那一分支, 它的类型也是 never.

译注: 类型护卫首次出现在高级数据类型一章.

1
2
3
4
5
6
7
8
9
function narrowByTypeGuard(x: number | string) {
if (typeof x === 'number') {
x;
} else if (typeof x === 'string') {
x;
} else {
x; // Acquire type never
}
}

never 是其他所有类型的子类型(可赋值给所有类型); 同时, 没有类型是 never 的子类型, 因此, 任何除 never 外的值都不可以赋值给一个 never, 包括 any.

Object

object 可以表示任何非原始数据类型(任何除 number, string, boolean, symbol, null, 和 undefined 外的).

有了 object, 我们能更好地表达形如 Object.create 的方法:

1
2
3
4
5
6
7
8
9
declare function create(o: object | null): void;

create({ prop: 0 }); // OK
create(null); // OK

create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error

类型担保

有时, 你可能比 TypeScript 更了解一个值.
特别地, 当你知道一个值的实际类型比它当前类型(译注: 引用它的变量类型是该值的父类型)更具体时.

类型担保打开了与编译器协商的通道: “相信我, 我知道我在干什么. ”
类型担保类似其他语言的强制类型转换, 但它既不重组数据(restructuring), 又不做任何运行时检查.
换言之, 它完全在编译期实现, 不加重运行期负担.
如果你使用类型担保, TypeScript 假定你 — 作为编程人员, 已经做了必要的检查.

有两种类型担保形式.

其一, 尖括号式:

1
2
3
let someValue: any = "this is a string";

let strLength: number = (<string>someValue).length;

其二, as 关键字式:

1
2
3
let someValue: any = "this is a string";

let strLength: number = (someValue as string).length;

以上两种写法完全等同.
一般, 选择哪种取决于个人偏好. 但当你结合 TypeScript, JSX 使用时, 只有 as 关键字式是有效的.

关于 let

到目前为止, 我们一直用 let 关键字代替或许你更熟悉的 var.
ES2015 为 JavaScript 引入 let 关键字, 它比 var 更安全, 所以正在逐渐成为标准写法.
我们以后再讨论其细节, 建议你尽可能用 let 代替 var, 它能减轻很多常见 JavaScript 问题.

如果这篇文章对您有用,可以考虑打赏:)