TypeScript 介绍

TypeScript(简称:TS)是 JavaScript 的超集(JS 有的 TS 都有)。

TypeScript = Type + JavaScript(在 JS 基础之上,为 JS 添加了类型支持)。

1
2
3
4
5
// TypeScript 有明确的类型
let age1: number = 18

// JavaScript 无明确的类型
let age2 = 18

TypeScript 是微软开发的开源编程语言,可以在任何运行 JavaScript 的地方运行。

TS 属于静态类型的编程语言,即先编译检查再执行;JS 属于动态类型,即执行时检查。

TypeScript 相比 JS 的优势

  1. 更早(写代码的同时)发现错误,减少找 Bug、改 Bug 时间,提升开发效率。
  2. 程序中任何位置的代码都有代码提示,随时随地的安全感,增强了开发体验。
  3. 强大的类型系统提升了代码的可维护性,使得重构代码更加容易。
  4. 支持最新的 ECMAScript 语法,优先体验最新的语法,让你走在前端技术的最前沿。
  5. TS 类型推断机制,不需要在代码中的每个地方都显示标注类型,让你在享受优势的同时,尽量降低了成本。

TypeScript 使用

安装编译 TS 的工具包

因为浏览器和 Node.js 不能识别 TS 代码,所以通过工具包编译 TS 为 JS 代码

image.png

1
npm i -g typescript

验证是否安装成功:tsc -v (查看 TypeScript 版本)

编译运行 TS 代码

  1. 编写 .ts 文件
  2. 将 TS 代码编译为 JS 代码
  3. 执行 JS 代码
1
2
tsc hello.ts
node hello.js

简化运行 TS 的步骤

安装 ts-node 包,直接在 Node.js 中执行 TS 代码

1
2
npm i -g ts-node
ts-node hello.ts

TypeScript 常用类型

类型注解

1
2
let age1: number = 18
let age2: number = '18' // 会报错

说明:代码中的 : number 就是类型注解。

作用:为变量添加类型约束。比如,上述代码中,约定变量 age 的类型为 number(数值类型)。

解释:约定了什么类型,就只能给变量赋值该类型的值,否则,就会报错。

常用基础类型概述

  1. JS 已有类型
  • 原始类型:number/string/boolean/null/undefined/symbol
  • 对象类型:object(包括,数组、对象、函数等对象)。
  1. TS 新增类型
  • 联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any 等。

原始类型

原始类型:number/string/boolean/null/undefined/symbol

1
2
3
let age: number = 18
let name: string = 'Alien'
let isHandsome: boolean = true

与 JavaScript 代码中的类型一致

数组类型

数组类型的两种写法: (推荐使用 number[] 写法)

1
2
let number: number[] = [1, 3, 5]
let strings: Array<string> = ['a', 'b', 'c']

当数组中有其他类型时,可以使用联合类型

1
let arr: (number | string)[] = [1, 'a', 2, 'b']

类型别名

类型别名(自定义类型):为任意类型起别名。

使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用。

1
2
type CustomArray = (number | string)[]
let arr1: CustomArray = [1, 'a', 2, 'b']
  1. 使用 type 关键字来创建类型别名。
  2. 类型别名,可以是任意合法的变量名称。
  3. 创建类型别名后,直接使用该类型别名作为变量的类型注解即可。

函数类型

函数的类型实际上指的是:函数参数和返回值的类型。

为函数指定类型的两种方式:

  1. 单独指定参数、返回值的类型
  2. 同时指定参数、返回值的类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
// 单独指定参数、返回值的类型:
function add(num1: number, num2: number): number {
  return num1 + num2
}
const add2 = (num1: number, num2: number): number => {
  return num1 + num2
}
// 同时指定参数、返回值的类型:
const add3: (num1: number, num2: number) => number = (num1, num2) => {
  return num1 + num2
}

console.log(add(1, 2), add2(2, 3), add3(3, 6));

解释:当函数作为表达式时,可以通过类似箭头函数形式的语法来为函数添加类型。

注意:同时指定的形式只适用于函数表达式。

当没有返回值时,函数返回类型为:void

1
2
3
4
function say(name: string): void {
  console.log('Hello', name);
}
say('TypeScript')

可选参数

使用函数实现某个功能时,参数可以传也可以不传。这种情况下,在给函数参数指定类型时,就用到可选参数了。

1
2
3
4
5
6
7
function say(name: string, count?: number): void {
  count = count ? count : 1
  for(let i = 0; i < count; i++){
    console.log('Hello', name);
  }
}
say('TypeScript')

可选参数:在可传可不传的参数名称后面添加 ?(问号)。

注意:可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数。

对象类型

  1. 直接使用 {} 来描述对象结构。属性采用属性名: 类型的形式;方法采用方法名(): 返回值类型的形式。
  2. 如果方法有参数,就在方法名后面的小括号中指定参数类型(比如:greet(name: string): void)。
  3. 在一行代码中指定对象的多个属性类型时,使用 ;来分隔。
  • 如果一行代码只指定一个属性类型(通过换行来分隔多个属性类型),可以去掉 ;
  • 方法的类型也可以使用箭头函数形式(比如:{ sayHi: () => void })。
1
2
3
4
5
6
let person: {name: string; age: number; sayHi(): void; sex?: boolean} = {
  name: 'Alien',
  age: 18,
  sex: true,
  sayHi() {},
}

对象属性或方法也可以使用可选属性,用?来表示

接口

当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的。

  1. 使用 interface 关键字来声明接口。
  2. 接口名称,可以是任意合法的变量名称。
  3. 声明接口后,直接使用接口名称作为变量的类型。
  4. 因为每一行只有一个属性类型,因此,属性类型后没有 ;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface Person {
  name: string
  age: number
  sayHi(): void
}

let handsomeBoy: Person = {
  name: 'Alien',
  age: 18,
  sayHi() {
    console.log('Hello, I\'m', this.name);
  }
}
handsomeBoy.sayHi()

interface 和 type 的对比

  1. 相同点:都可以给对象指定类型。
  2. 不同点:
  • 接口,只能为对象指定类型。
  • 类型别名,不仅可以为对象指定类型,实际上可以为任意类型指定别名。

继承:如果两个接口之间有相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用。

  1. 使用 extends(继承)关键字实现了接口 Point3D 继承 Point2D。
  2. 继承后,Point3D 就有了 Point2D 的所有属性和方法(此时,Point3D 同时有 x、y、z 三个属性)。
1
2
interface Point2D { x: number; y: number }
interface Point3D extends Point2D { z: number }

元组

元组类型是另一种类型的数组,能够指定包含元素个数,以及特定索引对应的类型

1
2
3
4
// 普通数组写法
let nums: number[] = [1, 666, 9]
// 元组写法
let nums: [number, number] = [1, 666]

类型推理

在 TS 中,某些没有明确指出类型的地方,TS 的类型推论机制会帮助提供类型。

常见场景:

  1. 声明变量并初始化时
  2. 决定函数返回值时。

image.png

image.png

注意:这两种情况下,类型注解可以省略不写!

类型断言

当需要更明确一个值的类型,可以使用类型断言来指定更具体的类型

1
2
3
4
5
6
// 被识别为 const aLink: HTMLElement,没办法使用a标签的href等属性
const aLink = document.getElementById('link')

// 使用类型断言指定更加具体的类型,两种方法
const aLink = document.getElementById('link') as HTMLAnchorElement
const aLink = <HTMLAnchorElement>document.getElementById('link')
  1. 使用 as 关键字实现类型断言。
  2. 关键字 as 后面的类型是一个更加具体的类型(HTMLAnchorElement 是 HTMLElement 的子类型)。
  3. 通过类型断言,aLink 的类型变得更加具体,这样就可以访问 a 标签特有的属性或方法了。

字面量类型

1
2
let str1 = 'Hello TS'
const str2 = 'Hello TS'

通过 TS 类型推理机制:

  1. str1 的类型为: string,因为是一个变量,它的值可以是任意字符串
  2. str2 的类型为: ‘Hello TS’,因为是一个常亮,其值只能是 ‘Hello TS’,这就是字面量类型

除字符串外,任意的 JS 字面量(比如,对象、数字等)都可以作为类型使用。

使用模式:字面量类型配合联合类型一起使用。

使用场景:用来表示一组明确的可选值列表。例如游戏当中的方向键只有上下左右。

1
2
3
function change(direction: 'up' | 'down' | 'left' | 'right')  {
console.log(direction)
}

参数 direction 的值只能是 up/down/left/right 中的任意一个。

优势:相比于 string 类型,使用字面量类型更加精确、严谨。

枚举

枚举可以理解为字面量类型+联合类型组合,可以表示一组明确的可选值

枚举:定义一组命名常量。描述一个值,该值可以是这些命名常量中的一个

1
2
3
4
5
6
enum Direction { Up, Down, Left, Right }

function changeD(direction: Direction) {
  console.log(direction);
}
changeD(Direction.Down)
  1. 使用 enum 关键字定义枚举。
  2. 约定枚举名称、枚举中的值以大写字母开头。
  3. 枚举中的多个值之间通过 , 分隔。
  4. 定义好枚举后,直接使用枚举名称作为类型注解。
  5. 访问枚举成员,可以通过.语法访问

image.png

枚举成员是有值的,默认从0开始递增。

数字枚举:枚举成员的值是数字的枚举,可以自定义枚举成员值

1
2
3
4
// Down -> 11, Left -> 12, Right -> 13
enum Direction { Up = 10, Down, Left, Right }
// 也可以全部自定义
enum Direction { Up = 2, Down = 4, Left = 6, Right = 8 }

字符串枚举:枚举成员的值是字符串的枚举

1
2
3
4
5
6
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}

注意:字符串枚举没有自增长行为,所以每个字符串枚举的成员必须有初始值

因为枚举不值用于类型,同时还提供值,所以在编译阶段会被编译成 JS 代码

image.png

any 类型

any 类型可以是该值进行任何类型操作,并且不会有代码其实

1
2
let obj: any = { x: 0 }
obj.bar = 100

注意:!!不推荐使用 any 类型!!,因为这样会让 TS 失去意义,可能造成不必要的错误

隐式的 any 类型情况:

  1. 声明变量不提供类型也不提供默认值;
  2. 函数参数不加类型

typeof

typeof 操作符能像 JS 一样,获取数据的类型

同时当出现在类型注解的位置时,可以在类型上下文中引用变量或属性的类型

1
2
3
4
5
let p = { x: 1, y: 2}
// 普通的类型注解方法
function func(point: { x: number; y: number}) {}
// 引用已有变量的值,用来简化类型书写
function func(point: typeof p) {}

注意:typeof 只能查询变量或属性的类型,无法查询其他形式的类型