TypeScript基础教程 -- 数据类型

markdown

TypeScript是JavaScript的一个超集,主要提供了类型系统和对ES6的支持,它有Microsoft开发,代码开源在github上。

引用官方的定义: TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.Any browser. Any host. Any OS. Open source.

翻译成中文即是: TypeScript是JavaScript的类型的超集,它可以编译成纯JavaScript。编译出来的JavaScript可以运行在任何浏览器上。TypeScript编译工具可以运行在任何服务器和操作系统上。TypeScript是开源的。

为什么选择TypeScript

这里总结一下其优势:

TypeScript增加了代码的可读性和可维护性

  • 类型系统实际上是最好的文档,大部分函数看看类型的定义就可以知道如何使用了
  • 可以在编译阶段就发现大部分错误,这总比在运行是出错好
  • 增强了编辑器和IDE的功能,包括代码补全、接口提示、跳转到定义、重构等

TypeScript非常包容

  • TypeScript是JavaScript的超集,.js 文件可以直接明明为 .ts 即可
  • 即使不显式的定义类型,也能够自动做出类型推断
  • 可以定义从简单到复杂的一切类型
  • 即使TypeSript编译报错,也可以生成JavaScript文件
  • 兼容第三方库,即使第三方库不是用TypeScript写的,也可以编写单独的类型

TypeScript拥有活跃的社区

  • 大部分第三方库都提供给TypeScript的类型定义文件
  • Google开发的Angular2就是使用TypeScript编写的
  • ES6的一部分特性是借鉴的TypeScript的
  • TypeScript拥抱了ES6规范,也支持部分ES7草案的规范

也说些缺点:

  • 有一定的学习成本,需要理解接口(interfaces)、泛型(generics)、类(classes)、枚举类型(enums)等前端工程师可能不是很熟悉的东西。而且中文资源不多
  • 短期可能会增加一些开发成本,毕竟要多写一些类型的定义,不过对于一个需要长期维护的项目,TypeScript能够减少其维护成本
  • 集成到构建流程需要一些工作量
  • 可能和一些库结合的不是很完美

总之可以根据自己团队和项目的情况判断是否需要使用TypeScript。

安装TypeScript请自行百度

Hello TypeScript

我们从一个简答的例子开始。将以下代码复制到 hello.ts 中:

1
2
3
4
5
function sayHello(person: string) {
return 'Hello,' + person;
}
let user = 'Tom';
console.log(sayHello(user));

然后执行

1
tsc hello.ts

这是后会生成一个编译好的文件

1
2
3
4
5
function sayHello(person) {
return 'Hello,' + person;
}
var user = 'Tom';
console.log(sayHello(user));

上述例子中,我们用 : 指定 person 参数类型为 string 。但是编译为 js 之
后,并没有什么检查的代码被插入进来。
TypeScript只会进行静态检查,如果发现有错误,编译的时候会报错。

下面尝试吧这段代码编译一下:

1
2
3
4
5
function sayHello(person: string) {
return 'Hello, ' + person;
}
let user = [0, 1, 2];
console.log(sayHello(user));

编辑器中会提示错误,编译的时候也会出错:

index.ts(6,36): error TS2345: Argument of type ‘number[]’ is not assignable to parameter of type ‘string’.

但是还是生成了 js 文件:

1
2
3
4
5
function sayHello(person) {
return 'Hello, ' + person;
}
var user = [0, 1, 2];
console.log(sayHello(user));

TypeScript编译的时候即使报错了,还是会生成编译结果,我们仍然可以使用这个编译后的文件

如果要在报错的时候终止 js 文件的生成,可以在 tsconfig.json 中配置 noEmitOnError 即可。关于 tsconfig.json ,请参阅官方手册(中文版)。

原始数据类型

JavaScript的类型分为两种:原始数据类型和对象类型。
原始数据类型包括:布尔值、数值、字符串、null、undefined、以及ES6中的新类型Symbol
本节主要介绍前五种原始数据类型在TypeScript中的应用。

布尔值

布尔值是最基础的数据类型,在TypeScript中,使用 boolean 定义布尔值类型:

1
2
let isDone: boolean = false;
// 编译通过

注意,使用构造函数 Boolean 创建的对象不是布尔值:

1
2
3
let createByNewBoolean: boolean = new Boolean(1);
// 编译不通过
// index.ts(1,5): error TS2322: Type 'Boolean' is not assignable to type 'boolean'.

事实上 new Boolean() 返回的是一个 Boolean 对象,应该这样定义它的类型:

1
let createByNewBoolean: Boolean = new Boolean(1);

直接调用 Boolean 可以返回一个 boolean 类型:

1
let createByBoolean: boolean = Boolean(1);

在TypeScript中,boolean是JavaScript中的基本类型,而Boolean是JavaScript中的构造函数。其他基本类型(除了null和undefined)一样,不在赘述。

数值

使用 number 定义数值类型:

1
2
3
4
5
6
7
8
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
// ES6 中的二进制表示法
let binaryLiteral: number = 0b1010;
// ES6 中的八进制表示法
let octalLiteral: number = 0o744;
let notANumber: number = NaN;
let infinityNumber: number = Infinity;

编译结果为:

1
2
3
4
5
6
7
8
var decLiteral = 6;
var hexLiteral = 0xf00d;
// ES6 中的二进制表示法
var binaryLiteral = 10;
// ES6 中的八进制表示法
var octalLiteral = 484;
var notANumber = NaN;
var infinityNumber = Infinity;

其中 0b1010 和 0o744 是 ES6 中的二进制和八进制表示法,它们会被编译为十进制数字。ES5中的语法不会被转换。

字符串

使用 string 定义字符串:

1
2
3
4
let myName: string = 'Xcat Liu';
let myAge: number = 25;
// 模板字符串 注意这里是 tab 上边那个键,而不是单引号
let sentence: string = `Hello, my name is ${myName}.I'll be ${myAge + 1} years old next month.`;

编译结果:

1
2
3
4
var myName = 'Xcat Liu';
var myAge = 25;
// 模板字符串
var sentence = "Hello, my name is " + myName + ".\nI'll be " + (myAge + 1) + " years old next month.";

空值

JavaScript中没有空值的概念,在TypeScript中,可以用 void 表示没有任何返回值的函数:

1
2
3
function alertName(): void {
alert('Tom');
}

使用 void 声明一个变量没什么用处,因为你只能把它赋值为 nullundefined

Null 和 Undefined

在 TypeScrip 中可以使用 nullundefined 来定义这两个原始数据类型:

1
2
let u: undefined = undefined;
let n: null = null;

undefined类型的变量只能被赋值为 undefined,null类型的变量只能被赋值为 null
与 void 类型的区别是 undefined 和 null 是所有类型的子类,也就是说 undefined 类型的变量可以赋值给 number/string… 类型的变量:

1
2
let num: number = undefined;
// 这样写不会报错 编译结果为: var num = undefined;

而 void 类型的变量不能赋值给 number 类型的变量:

1
2
3
let u: void;
let num: number = u;
// 编译报错:hello.ts(2,5): error TS2322: Type 'void' is not assignable to type 'number'.

任意值

任意值(any) 来表示允许赋值为任意类型

什么是任意值类型?

如果是普通类型,在赋值过程中改变其类型是不被允许的:

1
2
3
let num: number = 7;
num = 'strimg';
// hello.ts(2,1): error TS2322: Type '"strimg"' is not assignable to type 'number'.

而如果是 any 类型,则允许被赋值为任意类型:

1
2
let num: any = 7;
num = 'string';

未声明类型的变量

未声明类型的变量会被识别为 any 类型:

1
2
3
4
let something;
something = 7;
something = 'string';
something.setName('Tom');

相当于:

1
let something: any;

类型推断

在 2.1 版本之后,增加了 类型推断 这一概念。如果没有明确指定类型,TypeScript会按照类型推断的规则来推断出一个类型。
以下代码没有指定类型,编译时会报错:

1
2
3
let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
// hello.ts(2,1): error TS2322: Type '7' is not assignable to type 'string'.

在TypeScript 2.1 之前,如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查。TypeScript 2.1 中,编译器会考虑对 myFavoriteNumber 的最后一次赋值来检查类型。

联合类型

联合类型表示取值可以为多种类型中的一种。举个例子:

1
2
3
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

如果这样写就会报错:

1
2
3
let myFavoriteNumber: string | number;
myFavoriteNumber = true;
// hello.ts(2,1): error TS2322: Type 'true' is not assignable to type 'string | number'.

联合类型使用 | 分割每个类型。这里 string | number 的含义是允许 myFavoriteNumber 的类型时 string 或 number 而不能时其他类型。

访问联合类型的属性和方法

当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法:

1
2
3
4
function getLength(something: string | number): number {
return something.length;
}
// hello.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.Property 'length' does not exist on type 'number'.

因为 length 不是 number 和 string 的共有属性,所以会报错。访问 toString() 就没有问题:

1
2
3
function getString(something: string | number): string {
return something.toString();
}

联合类型的变量在赋值时也会根据类型推断的规则推断出一个类型:

1
2
3
4
5
6
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
console.log(myFavoriteNumber.length); //5
myFavoriteNumber = 7;
console.log(myFavoriteNumber.length); //报错
// hello.ts(5,30): error TS2339: Property 'length' does not exist on type 'number'.

上例,在第二行 myFavoriteNumber 被推断为 string,访问其 length 属性不会报错,而第四行 myFavoriteNumber 被推断为 number,访问其 length 属性就报错了。