第 1 节 概述
TypeScript 由微软开发,是基于 JavaScript 的一个扩展语言。TypeScript 中增加了静态类型检查、接口、泛型等现代开发特性,更适合大型项目的开发。
TypeScript 无法直接在浏览器中运行,需要先编译为 JavaScript,编译前需要安装环境:
1
2
|
# 全局安装 typescript
npm install typescript -g
|
可以对单个文件进行编译:
也可以设置为自动化编译:
1
2
3
4
|
# 创建编译控制⽂件 tsconfig.json
tsc --init
# 监视⽬录中的 .ts ⽂件变化
tsc --watch
|
一般来说,现代的前端框架会自动对 TypeScript 进行编译。我们无需自己编译 TypeScript 文件。
第 2 节 基础类型
最简单的类型,通常作为更复杂类型的组成部分,分为字符串、数值和布尔值三类。
1
2
3
|
let name: string = "Tom"
let age: number = 18
let isLogin: boolean = true
|
变量的取值严格限制为某一个确定值时,可以使用字面量类型,让“值本身”成为类型的一部分。
1
|
let direction: "left" = "left"
|
第 3 节 拼接类型
拼接类型是由多个类型拼接而成的复杂类型。
3.1 类型推断
TypeScript 会对变量的类型进行推断,即便我们不写变量类型,也会进行静态类型检查。
1
2
|
// TypeScript 会推断出变量 a 的类型是数值
let a = 10
|
在我们写拼接类型时,可以依据类型推断,不影响语义的同时,简化变量的申明步骤。
3.2 函数类型
1
2
3
4
5
6
7
|
// 可以在申明时指定数据类型
let count: (a: number, b: number) => number
count = function (x, y) { return x + y }
// 也可以在创建时指定数据类型
let count = function (x: number, y: number): number {
return x + y
}
|
3.3 对象类型
type 可以为任意类型创建别名,可以方便地进行类型复用和扩展。适用于定义需要多次使用的类型。
1
2
3
|
// 必须有 name 属性,age 为可选属性
type Person = { name: string, age?: number }
let person: Person = {name: '张三'}
|
3.4 联合类型
表示允许取几种类型中的任意一种,比如“字面量”就是一种特殊的类型:
1
2
3
|
// 性别这个类型就比较常用,定义为男或女
type Gender = '男' | '女';
let gender: Gender = '男'
|
3.5 交叉类型
表示要满足全部的要求。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//⾯积
type Area = {
height: number; //⾼
width: number; //宽
};
//地址
type Address = {
num: number; //楼号
cell: number; //单元号
room: string; //房间号
};
// 定义类型 House ,且 House 是 Area 和 Address 组成的交叉类型
type House = Area & Address;
const house: House = {
height: 180,
width: 75,
num: 6,
cell: 3,
room: '702'
};
|
3.6 枚举类型
枚举类型用来表示有限的、固定的类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
enum Direction {
Up,
Down,
Left,
Right,
}
function walk(n: Direction) {
if (n === Direction.Up) {
console.log("向上⾛");
} else if (n === Direction.Down) {
console.log("向下⾛");
} else if (n === Direction.Left) {
console.log("向左⾛");
} else if (n === Direction.Right) {
console.log("向右⾛");
} else {
console.log("未知⽅向");
}
}
walk(Direction.Up)
let x = Direction.Down
|
3.7 数组类型
数组可以直接定义,也可以通过泛型方式进行定义:
1
2
3
4
5
|
let arr1: string[]
let arr2: Array<string>
arr1 = ['a','b','c']
arr2 = ['hello','world']
|
3.8 元组类型
当一个变量本质上是数组,但每个位置都有明确含义时,使用元组会比普通数组更清晰。
1
2
3
|
//// 第⼆个元素可选,如果存在,必须是 boolean 类型。
type Count = [number, boolean?]
let count: Count = [10, true]
|
第 4 节 输入/输出
4.1 无返回值
void 表示函数无返回值。
1
2
3
|
function log(msg: string): void {
console.log(msg)
}
|
4.2 不可信输入
在处理外部输入时,不清楚其类型,可以从 unknow 开始,先验证再使用。
1
2
3
4
5
|
function handle(input: unknown) {
if (typeof input === "string") {
input.toUpperCase()
}
}
|
4.3 穷尽检查
never 表示当所有可能情况都被处理完之后,仍然不应该存在的分支,一般由 TypeScript 推断出来,可以借助 never 发现程序中无法执行或者不会有任何返回值(比如抛出异常)的分支。
1
2
3
4
5
6
7
8
9
10
11
12
|
type Action = "add" | "remove"
function handleAction(a: Action) {
switch (a) {
case "add":
return
case "remove":
return
default:
const _never: never = a
}
}
|
第 5 节 类
5.1 属性修饰符
| 修饰符 |
含义 |
具体规则 |
| public |
公开的 |
可被类内部、子类、类外部访问。 |
| protected |
受保护的 |
可被类内部、子类访问。 |
| private |
私有的 |
可被类内部访问。 |
| readonly |
只读属性 |
属性无法修改。 |
5.2 类
在 JavaScript 中类的属性需要在构造函数外部申明,在 TypeScript 中可以只在构造函数中申明一次。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class Person {
constructor(
// name 为 readonly 属性,无法修改
public readonly name: string,
// age 为 protected 属性,只能在类内部和子类中使用
protected age: number,
// IDCard 为 private 属性,只能在类内部使用
private IDCard: string
) {
}
private getPrivateInfo() {
// 类内部可以访问 private 属性
return `身份证号码为: ${this.IDCard}`
}
getInfo() {
return `我叫: ${this.name},今年${this.age}岁`;
}
}
|
5.3 接口
interface 为类、对象、函数等定义格式,不能包含任何实现。interface 和 type 的功能十分接近,很多场景下可以互换,区别在于:
interface 更专注于定义对象和类的结构,支持继承、合并。
type可以定义类型别名、联合类型、交叉类型,但不支持继承和自动合并。
1
2
3
4
5
6
7
8
9
10
11
12
|
// 使⽤ interface 定义 Person 对象
interface PersonInterface {
name: string;
age: number;
speak(): void;
}
// 使⽤ type 定义 Person 对象
type PersonType = {
name: string;
age: number;
speak(): void;
};
|
接口可以实现继承、合并:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
interface PersonInterface {
name: string; // 姓名
age: number; // 年龄
}
interface PersonInterface {
speak: () => void;
}
interface StudentInterface extends PersonInterface {
grade: string; // 年级
}
const student: StudentInterface = {
name: '李四',
age: 18,
grade: '高二',
speak() { console.log(this.name, this.age, this.grade); }
}
|
type 也可以通过交叉类型实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
// 使⽤ type 定义 Person 类型,并通过交叉类型实现属性的合并
type PersonType = {
name: string; // 姓名
age: number; // 年龄
} & {
speak: () => void;
};
// 使⽤ type 定义 Student 类型,并通过交叉类型继承 PersonType
type StudentType = PersonType & {
grade: string; // 年级
};
const student: StudentType = {
name: '李四',
age: 18,
grade: '⾼⼆',
speak() {
console.log(this.name, this.age, this.grade);
}
};
|
5.4 抽象类
如果某一类对象不仅有结构要求,还有一部分共同行为,同时又希望子类必须补充关键逻辑,就可以使用抽象类。抽象类和接口的区别在于:
- 接口:只能描述结构,不能有任何实现代码,一个类可以实现多个接口。
- 抽象类:既可以包含抽象方法,也可以包含具体方法,一个类只能继承一个抽象类。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
abstract class Animal {
move(): void {
console.log("moving")
}
abstract speak(): void
}
class Dog extends Animal {
speak() {
console.log("wang")
}
}
|
第 6 节 泛型
泛型允许我们在定义函数、类或接口时,使用类型参数来表示未指定的类型,这些参数在具体使用时,才被指定具体的类型。泛型能让同一段代码适用于多种类型。同时仍然保持类型的安全性。
1
2
3
4
5
|
function logData<T, U>(data1: T, data2: U): void {
console.log(data1, data2)
}
logData<number, string>(100, 'hello')
|
可以为泛型设置一定的约束条件:
1
2
3
4
5
6
7
8
9
10
|
// 泛型约束
TypeScript
interface LengthInterface {
length: number
}
// 传入的类型 T 必须具有 length 属性
function logPerson<T extends LengthInterface>(data: T): void {
console.log(data.length)
}
|