# TypeScript入门

# 什么是TypeScript?

TypeScript是JavaScript的一个超集,提供了类型系统和对ES6的支持,它可以编译成纯JavaScript。 TypeScript由Microsoft开发且开源。

# 为什么选择TypeScript?

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

类型系统实际上是最好的文档,大部分的函数看看类型的定义就可以知道如何使用了

可以在编译阶段就发现大部分错误,这总比在运行时候出错好

增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等

2、TypeScript非常包容

TypeScript 是 JavaScript 的超集,.js 文件可以直接重命名为 .ts 即可

即使不显式的定义类型,也能够自动做出类型推论

可以定义从简单到复杂的几乎一切类型

即使 TypeScript 编译报错,也可以生成 JavaScript 文件

兼容第三方库,即使第三方库不是用 TypeScript 写的,也可以编写单独的类型文件供 TypeScript 读取

3、拥有活跃的社区

# 使用TypeScript

npm install -g typescript //安装TypeScript
tsc hello.ts              //编译TypeScript文件,这时候会生成一个编译好的文件hello.js
1
2

TypeScript 中,使用 : 指定变量的类型,: 的前后有没有空格都可以。

function sayHello(person: string) {
    return 'Hello, ' + person;
}

let user = 'Tom';
console.log(sayHello(user));
1
2
3
4
5
6

上述例子中,我们用 : 指定 person 参数类型为 string。let是ES6中的关键字,和var类似,用于定义一个局部变量,

所声明的变量,只在let命令所在的代码块内有效。据此,for循环的计数器,就很合适使用let命令。另外,let所声明的变量一定要在声明后使用,否则报错。

# 原始数据类型

JavaScript 的类型分为两种:原始数据类型和对象类型。

原始数据类型包括:布尔值、数值、字符串、null、undefined 以及 ES6 中的新类型 Symbol。

# 在 TypeScript 中,使用 boolean 定义布尔值类型:

let isDone: boolean = false;
1

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

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

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

let createdByBoolean: boolean = Boolean(1);
1

在 TypeScript 中,boolean 是 JavaScript 中的基本类型,而 Boolean 是 JavaScript 中的构造函数。

其他基本类型(除了 null 和 undefined)一样,不再赘述。

# 数值

使用 number 定义数值类型:

let decLiteral: number = 6;
1

# 字符串

使用 string 定义字符串类型:

let myName: string = 'Tom';
let myAge: number = 25;

// 模板字符串
let sentence: string = `Hello, my name is ${myName}.
I'll be ${myAge + 1} years old next month.`;
1
2
3
4
5
6

其中用来定义ES6中的模板字符串(ES6 引入了模板字符串,用反引号标识,它可以当作普通字符串使用,

也可以用来定义多行字符串,或者在字符串中嵌入变量),${expr} 用来在模板字符串中嵌入表达式。

# 空值

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

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

# Null 和 Undefined

在 TypeScript 中,可以使用 null 和 undefined 来定义这两个原始数据类型:

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

undefined 类型的变量只能被赋值为 undefined,null 类型的变量只能被赋值为 null

# 任意值类型

any 类型允许被赋值为任意类型。如果是一个普通类型,在赋值过程中改变类型是不被允许的。

let myFavoriteNumber: any = 'seven';
myFavoriteNumber = 7;
1
2

在任意值上访问任何属性都是允许的:

let anyThing: any = 'hello';
console.log(anyThing.myName);
console.log(anyThing.myName.firstName);
1
2
3

也允许调用任何方法:

let anyThing: any = 'Tom';
anyThing.setName('Jerry');
anyThing.setName('Jerry').sayHello();
anyThing.myName.setFirstName('Cat');
1
2
3
4

可以认为,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。 变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型:

let something;
something = 'seven';
something = 7;

something.setName('Tom');
1
2
3
4
5

等价于

let something: any;
something = 'seven';
something = 7;

something.setName('Tom');
1
2
3
4
5

# 类型推论

以下代码虽然没有指定类型,但是会在编译的时候报错:

let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
1
2

事实上,它等价于:

let myFavoriteNumber: string = 'seven';
myFavoriteNumber = 7;
1
2

TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论。 如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查。

# 联合类型

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

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

这里的 let myFavoriteNumber: string | number 的含义是,允许 myFavoriteNumber 的类型是 string 或者 number,但是不能是其他类型。

# 对象的类型——接口

在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。

interface Person {
    name: string;
    age: number;
}

let tom: Person = {
    name: 'Tom',
    age: 25
};
1
2
3
4
5
6
7
8
9

上面的例子中,我们定义了一个接口 Person,接着定义了一个变量 tom,它的类型是 Person。这样,我们就约束了 tom 的形状必须和接口 Person 一致。(接口一般首字母大写)

# 可选属性

有时我们希望不要完全匹配一个形状,那么可以用可选属性:

interface Person {
    name: string;
    age?: number;
}

let tom: Person = {
    name: 'Tom'
};
1
2
3
4
5
6
7
8

可选属性的含义是该属性可以不存在。

# 数组的类型

在 TypeScript 中,数组类型有多种定义方式,比较灵活。 「类型 + 方括号」表示法 最简单的方法是使用「类型 + 方括号」来表示数组:

let fibonacci: number[] = [1, 1, 2, 3, 5];
1

数组泛型 泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

//也可以使用数组泛型Array<elemType>来表示数组:
let fibonacci: Array<number> = [1, 1, 2, 3, 5];
1
2

接口也可以用来描述数组:

interface NumberArray {
    [index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
1
2
3
4

NumberArray 表示:只要 index 的类型是 number,那么值的类型必须是 number。

# 函数的类型

一个函数有输入和输出,要在 TypeScript 中对其进行约束,需要把输入和输出都考虑到,其中函数声明的类型定义较简单:

function sum(x: number, y: number): number {
    return x + y;
}
1
2
3

注意,输入多余的(或者少于要求的)参数,是不被允许的。 如果要我们现在写一个对函数表达式(Function Expression)的定义,可能会写成这样:

let mySum = function (x: number, y: number): number {
    return x + y;
};
1
2
3

这是可以通过编译的,不过事实上,上面的代码只对等号右侧的匿名函数进行了类型定义,而等号左边的 mySum,是通过赋值操作进行类型推论而推断出来的。如果需要我们手动给 mySum 添加类型,则应该是这样:

let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};
1
2
3

注意不要混淆了 TypeScript 中的 => 和 ES6 中的 =>。 在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。

在 ES6 中,=> 叫做箭头函数,ES6 允许使用“箭头”(=>)定义函数。以下为ES6箭头函数:

var f = v => v;

// 等同于
var f = function (v) {
  return v;
};
1
2
3
4
5
6

如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。

var f = () => 5;
// 等同于
var f = function () { return 5 };

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
  return num1 + num2;
};
1
2
3
4
5
6
7
8
9

具体可见ES6 中的箭头函数

# 类型断言

类型断言可以用来手动指定一个值的类型。 语法

<类型>
1

值 as 类型
1

在 tsx 语法(React 的 jsx 语法的 ts 版)中必须用后一种。 有时候,我们需要在还不确定类型的时候就访问其中一个类型的属性或方法。此时可以使用类型断言:

function getLength(something: string | number): number {
    if ((<string>something).length) {
        return (<string>something).length;
    } else {
        return something.toString().length;
    }
}
1
2
3
4
5
6
7

类型断言的用法如上,在需要断言的变量前加上 Type 即可。 类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的。