我为何会这样念念又不忘 / 你用什么牌的箭刺穿我心脏javascript
我也久经沙场 / 戎马生涯 / 依然 / 被一箭刺伤java
——李荣浩《念念又不忘》typescript
接下来我会分上、下两篇文章介绍 TypeScript。shell
我也是 TypeScript 初学者,这两篇文章是个人学习笔记,来源于一个系列的免费视频,视频不错,若是你以为看视频学习更快,能够点击这里看到。npm
TypeScript 与 JavaScript 有何区别呢?简单点说,就是前者引入了类型约束,能实现代码的静态检查,并且能提供更加完善的代码提示功能;除此以外,还引入了接口、抽象类、枚举、访问控制修饰符、泛型等语法,让咱们写出的代码更具健壮性和可扩展性。编程
上、下两篇的文章内容安排以下:数组
《上篇》定义为基础篇,《下篇》定义为深刻篇。bash
咱们先从基础篇开始学习。函数
TypeScript 脚本以 .ts
后缀结尾。我使用 VSCode 学习 TypeScript,写好 TypeScript 代码后, 须要将其编译为 JavaScript 代码才能运行。在此以前,咱们先要安装 Node.js 环境。学习
$ npm install -g typescript
$ tsc --version
复制代码
编译文件的指令以下:
// 执行完下列语句后,会在同级目录下看到一个 `HelloWorld.js` 文件,
// 就是编译完成后的文件啦
$ tsc HelloWorld.ts
复制代码
codepen.io 中也支持 TypeScript 的书写。新建一个新的 Pen 后,将 JavaScript 一栏的预处理器设置成 TypeScript 便可,你也能够实时查看到编译以后的代码,不过代码提示效果不是很强。
咱们先从最简单的基本数据类型(Primitive)提及。
ECMAScript 提供了六种基本数据类型:布尔、数值、字符串、Null、Undefined 和 Symbol。
而 TypeScript 针对上述的每一种类型,都提供了对应的类型字面值:boolean、number、string、null、undefiend 和 symbol(ES6 中引入,本系列两篇不对 symbol 作介绍)。
在 TypeScript 中,使用 : type
语法为变量指定类型:
// 声明一个布尔值类型变量 `isMale`,初始值为 `true`
let isMale: boolean = true;
// 声明一个字符串类型变量 `myName`,初始值为 `'Alex'`
let myName: string = 'Alex';
// 声明一个数值类型变量 `myAge`,初始值为 `20`
let myAge: number = 20;
// 声明一个 Null 类型变量 `myGirlFriend`,值为 `null`
let myGirlFriend: null = null;
// 声明一个 Undefined 类型变量 `myHouse`,值为 `undefined`
let myHouse: undefined = undefined;
复制代码
注意:TypeScript 提供的类型字面值都是小写形式,注意与首字母大写的形式区分,后者是 JavaScript 原生提供的构造器函数。
有时,一个变量的类型并不局限于一种。好比,一个变量的值能够是字符串,也能够是数值。这时就要用到“联合类型”了。
联合类型使用竖线 |
分隔,表示某个变量能够给予其中任意一种类型值。
下例中,声明了一个变量 foo
,它的值能够是一个字符串,也能够是一个数值。
// 此处声明了一个变量 `foo`,能够是字符串,也能够是数值
let foo: string | number = 'bar'; // 初始值给了字符串 `'bar'`
foo = 123; // 接下来将 `foo` 从新赋值为 `123`
复制代码
除了基本类型,工做中最常处理就是对象了。那么如何在 TypeScript 中指定对象类型呢?
在 TypeScript 中,使用接口,也就是关键字 interface
来描述对象的形状,也就是对象的类型。“接口”在传统的面向对象编程的语言里,好比 Java,表示“行为的抽象”,而在 TypeScript 对此稍有不一样,接口不只能够表示行为的抽象,还能够用来定义对象类型。
接下来,咱们定义一个类型 Person
(按照约定,首字母大写):
// 用接口声明一个类型 `Person`
interface Person {
name: string;
age: number;
}
// 将变量 `alex` 声明为 `Person` 类型
let alex: Person = {
name: 'Alex',
age: 20
};
复制代码
注意:定义接口时,属性之间能够用分号
;
、也能够用逗号,
分隔,甚至什么都不加也能够。
咱们定义了一个类型 Person
,并将变量 alex
的类型声明为 Person
。那么,在给 alex
赋值时,必须严格符合类型定义:赋值对象必须由一个字符串属性 name
和一个数值属性 age
组成,缺乏或多出的属性,都会提示错误。
// 会提示出错(缺乏一个属性)
let alex: Person = {
name: 'Alex';
};
// 会提示错误(多了一个属性)
let alex: Person = {
name: 'Alex',
age: 20,
gender: 'male'
};
复制代码
定义类型时,若是想要表示某个属性是可选的,则使用 ?: type
语法声明。
// 类型 `Person` 的 `name` 属性是可选的
interface Person {
name: string;
age?: number;
}
// 由于 `age` 是可选属性,因此赋值时不给也行
let alex: Person = {
name: 'Alex'
};
复制代码
除了定义可选属性,还能够定义“任意属性”。
所谓的任意属性,就是咱们不肯定未来会添加的属性名称是什么,可是会提早定义容许添加的属性,在不肯定将来这个属性名的状况下,限制这个属性的类型。
// Person 中定义了一个任意属性,属性类型是 `any`
interface Person {
name: string;
age?: number;
[propName: string]: any;
}
// 咱们给变量 `alex` 添加了一个任意属性 `gender`
let alex: Person = {
name: 'Alex',
gender: 'male
};
复制代码
咱们使用 [propName: string]: any
的形式,定义了一个 any
类型的任意属性。
注意,任意属性的类型,必须是上面的已知属性
name
和age
类型的超集,不然会提示出错。 好比,上面咱们能够将上面任意属性的类型any
修改成string | number
也是能够的。
说完对象,再来介绍数组。
在数组上指定类型,本质上是限制数组成员的类型。在 TypeScript 中,使用 type[]
语法指定数组成员的类型。
下面定义了一个数组,限制其成员只能是字符串。
// 此处定义了一个数组 `myFriends`,其成员限定为只能是字符串
let myFriends: string[] = ['Alex', 'Bob'];
复制代码
若是数组成员容许包含多个类型值,则使用 (type1 | type2 | ...)[]
的语法声明。
// 此处定义了一个数组 `foo`,其成员能够是字符串,也能够是数值
let foo: (string | number)[] = ['Alex', 'Bob', 123];
复制代码
若是数组的成员是对象,则有以下两种声明方式:
// 方式 1:经过预约义好的类型,声明 `friends` 成员类型
interface Person {
name: string;
}
let friends: Person[] = [ { name: 'Alex' }, { name: 'Bob' } ];
// 方式 2:直接经过字面量类型的形式,声明 `friends` 成员类型
let friends: {
name: string
}[] = [ { name: 'Alex' }, { name: 'Bob' } ];
复制代码
接下来,进入到扩展类型的学习。
Typescript 除了支持 JavaScript 类型以外,还提供了一些扩展类型。
首先,咱们来介绍下字面量类型。
当咱们像下面这样赋值时:
let seven: number = 7;
// ❌ 这样赋值的话会有错误,提示`Type '"Seven"' is not assignable to type 'number'`
seven = 'Seven';
复制代码
注意,这里的 'Seven'
被当成了一个类型,说 'Seven'
类型不能赋值给数值类型变量 seven
。
其实这里的 'Seven'
是一个字符串字面量类型。
Typescript 中的字面量类型包括:字符串字面量、数值字面量和布尔值字面量。
除此以外,咱们还可使用 type
关键字定义一个新的类型:
// 此处咱们定义了一个新类型 `FavoriteNumber`,这个新类型仅由三个值的集合组成
type FavoriteNumber = 'One' | 'Two' | 'Seven';
// 接下来,将变量 `seven` 声明为类型 `FavoriteNumber`,并赋值为 `'Seven'`
let seven: FavoriteNumber = 'Seven';
复制代码
以上定义了一个类型 FavoriteNumber
,它由三个值的集合组成(一个类型一般至少包含两个或以上的值)。变量 seven
被声明为该类型,赋值为 'Seven'
,这是一个有效值。若是咱们给 seven
赋了一个不在 FavoriteNumber
类型以内的值,就会报错,好比:
// ❌ 此处会报错:`Type '123' is not assignable to type 'FavoriteNumber'.`
let seven: FavoriteNumber = 123;
复制代码
在介绍枚举类型以前,咱们先来看下面的代码:
// 此处定义了两个变量 `errorColor` 和 `infoColor`
let dangerColor = 'red';
let infoColor = 'blue';
// 添加一个判断传入颜色是不是危险色的函数
function isItDangerColor(color) {
return color === 'red';
}
// 接下来,调用函数 `isItRed`
isItDangerColor(dangerColor); // true
isItDangerColor(infoColor); // false
复制代码
上面这一小段的代码逻辑很简单,但有个小小的问题——若是表示危险的颜色由 'red'
变为 'pink'
了,那么咱们就须要修改两个地方的代码。
针对这个问题,咱们稍微修改下代码,引入一个表示颜色集合的变量 Colors
来解决:
// 咱们使用 `Colors` 这个变量来存储逻辑中使用到的颜色集合
const Colors = {
Danger: 'red',
Info: 'blue'
};
// 在余下的业务逻辑中,咱们使用颜色变量代替以前的颜色字面值
let dangerColor = Colors.Danger;
let infoColor = Colors.Info;
function isItDangerColor(color) {
return color === Colors.Danger;
}
复制代码
这样带来的便利是,若是 Colors.Danger
所表明的颜色值变了,只要在 Colors
中修改一下就能够了。
进一步思考,能够知道,这里的 Colors.Danger
和 Colors.Info
的值具体是什么并不重要,只要能保证它们彼此不相等就行。好比,咱们写成下面这样:
// 这样定义 `Colors` 依旧不会影响逻辑
const Colors = {
Danger: 0,
Info: 1
};
复制代码
这种定义变量的方式,用 TypeScript 中的枚举来改写就是下面这样的:
// 枚举变量使用 `enum` 关键字定义
// 此处定义了一个枚举变量 `Colors`
enum Colors {
Danger,
Info
}
复制代码
上面一段代码通过编译后,获得的 JavaScript 源码以下:
var Colors;
(function (Colors) {
Colors[Colors["Danger"] = 0] = "Danger";
Colors[Colors["Info"] = 1] = "Info";
})(Colors || (Colors = {}));
复制代码
由此可知,
enum Colors {
Danger,
Info
}
// 等价于
var Colors = {
0: 'Danger',
1: 'Info',
'Danger': 0,
'Info': 1
};
复制代码
咱们修改下初始的例子,使用枚举来组织逻辑:
enum Colors {
Danger, // 对应的值是 0
Info, // 对应的值是 1
Success // 对应的值是 2
}
// 咱们将函数 `isItDanger` 的参数 `color` 类型约束为 `Colors`
// 说明此函数只接收 `Colors` 中列举的值
function isItDanger(color: Colors): boolean {
return color === Colors.Danger;
}
// 接下来使用 `Colors.Info` 调用 `isItDanger` 函数
isItDanger(Colors.Info); // false
复制代码
除了使用默认的索引值,咱们还能够为枚举变量中的每一项指定值:
enum Colors {
Red, // 对应的值是 0
Blue = 3, // 将 Blue 值指定为 3
Green // 接上面的 3,此处的值是 4
}
enum Colors {
Red = 'red', // 对应的值是 'red'
Blue = 'blue', // 对应的值是 'blue'
Green = 'green' // 对应的值是 'green'
}
复制代码
上篇完。