目录
一、TypeScript 优势
二、安装 TypeScript
三、TypeScript 基础类型
3.1 数组 2 种定义方式
3.2 元组 Tuple
3.3 枚举 enum
3.4 any 任何类型
3.5 void 没有任何类型
3.6 never 永不存在值的类型
3.7 使用 object 类型表示 Object.create API
3.8 类型断言 2 种形式
四、interface 接口
五、函数
六、类型推论、联合类型
七、Class 类、类与接口
八、泛型 Generics
九、类型别名和交叉类型
十、声明文件
十一、内置类型
一、TypeScript 优势
- JavaScript 的超集
- 支持 ES6 标准,并支持输出 ES 3 / 5 / 6 标准的纯 JS 代码
- 支持 ES 未来提按中的特性,比如异步功能和装饰器
- 支持类型系统且拥有类型推断
- 支持运行在任何浏览器 、Node.js 环境中
Node.js 是 JS 一个基于服务端的运行环境,带 LTS 标记是稳定版,node 版本查询指令 node -v
npm 全称是 Node package Manager,翻译过来是 Node.js 的包管理工具,npm 与 Node.js 一起无痕地捆绑安装
Yarn 是一款新的 JS 包管理工具
二、安装 TypeScript
// 全局安装 TypeScript
npm install -g typescript
// TypeScript 版本查询
tsc -v
// TypeScript 编译成 JavaScript
tsc test.ts
// ts 文件编译成 es6
tsc test.ts target --es2015
// 全局监听 TypeScript
tsc test.ts --watch
// ts-node 提供直接运行 TypeScript 代码的能力
ts-node test.ts
三、TypeScript 基础类型
原始数据类型 7 个:Boolean、String、Number、Null、Undefined、BigInt、Symbol
Null、Undefined 是任何类型的子类型
// const 定义的变量必须赋初始值
const isDone: boolean = true
const str: string = 'Hello TypeScript'
const num: number = 123
const n: null = null
const u: undefined = undefined
const big: bigint = 20n
const sym: symbol = Symbol('独一无二的值')
3.1 数组 2 种定义方式
// 在元素类型后面接上 [],表示由此类型元素组成的一个数组
const arr1: number[] = [1, 2, 3]
// 使用数组泛型,Array<元素类型>
const arr2: Array<number> = [1, 2, 3]
3.2 元组 Tuple
// 已知元素数量和类型的数组,各元素的类型不必相同,元组里面的元素有顺序要求
const tuple: [string, number, boolean] = ['str', 1, true]
3.3 枚举 enum
/**
* 枚举类型首字母大写 ,使用枚举类型可以为一组数值赋予友好的名字
* 枚举类型提供便利由枚举的值得到它的名字
* 枚举里面的值用逗号分隔
*/
// 数字枚举有正向映射(key->value)和反向映射(value->key),默认从 0 开始,也可以手动指定成员数值,后面的值依次递增
enum State {
pending,
fulfilled,
rejected
}
const pending = State.pending
const first = State[0]
console.log(pending, first) // 0 'pending'
// 字符串枚举只有正向映射没有反向映射,字符串枚举没有递增的含义,字符串枚举成员都必须手动初始化
enum Direction {
top = 'top',
right = 'right',
bottom = 'bottom',
left = 'left'
}
const r = Direction.right
// const r = Direction['right'] // 访问属性 2 种形式都可以
console.log(r) // right
3.4 any 任何类型
// any 可以给变量设置任何类型,typescript 不做类型检查
let which: any = 123
which = 'str'
which = true
const arr: any[] = [123, 'str', true, null, undefined]
3.5 void 没有任何类型
/**
* 当一个函数没有返回值时,通常会见到其返回值类型是 void
*/
function warnUser(): void {
console.log('This is my warning message')
}
warnUser()
3.6 never 永不存在值的类型
/**
* never 适用场景
* 抛出异常 try...catch
* 根本不会有返回值的函数表达式或箭头函数表达式的返回值类型
* 变量也可能是 never 类型,当它们被永不为真的类型保护所约束时
*
* never 类型是任何类型的子类型,也可以赋值给任何类型;
* 然而,没有类型是 never 的子类型或可以赋值给 never 类型(除了 never 本身之外)
* 即使 any 也不可以赋值给 never
*/
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message)
}
// 推断的返回值类型为never
function fail() {
return error("Something failed")
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {
}
}
3.7 使用 object 类型表示 Object.create API
declare function create(o: object | null): void
create({ prop: 0 })
create(null)
3.8 类型断言 2 种形式
/**
* 类型断言好比其它语言里的类型转换,不进行特殊的数据检查和解构
* 它没有运行时的影响,只是在编译阶段起作用
*/
// 使用 as 关键字
const val01: any = 'This is a string'
const len01: number = (val01 as string).length
// 使用“尖括号”:<元素类型>变量
const val02: any = 'This is a string'
const len02: number = (<string>val02).length
四、interface 接口
/**
* interface 接口名首字母大写,接口是一个对象对属性类型进行描述
* 接口定义函数参数的数据类型和函数参数的返回值类型
* 可选属性用 ? 表示
* readonly 只读属性,字段只能在创建的时候被赋值
* 只读变量用 const
*/
// 定义一个 Person 接口
interface Person {
name?: string
age: number
readonly height?: string
}
// 定义一个 student 变量,类型是 Person。约束 student 字段必须和 Person 字段一致
const student: Person = {
name: '张三',
age: 18,
height: '180cm'
}
student.name = '李四'
// student.height = '170cm'
console.log(student)
五、函数
// 约定输入、输出,可选参数
function add(x: number, y: number, z?: number): number {
if (typeof z === 'number') {
return x + y + z
} else {
return x + y
}
}
const result = add(1, 2, 3)
console.log(result)
// 函数本身类型
const add2: (x: number, y: number, z?: number) => number = add
// interface 接口描述函数类型
const sum = (x: number, y: number) => {
return x + y
}
interface ISum {
(x: number, y: number): number
}
const sum2: ISum = sum
六、类型推论、联合类型
/**
* 类型推论:根据值推论出变量的数据类型
* const 定义一个常量
*/
let str = 'str'
const num = 123
/**
* 联合类型:用中竖线分割
* 当 TS 不确定一个联合类型的变量到底是哪个类型的时候
* 只能访问此联合类型的所有类型里共有的属性或方法
*/
let numberOrString: number | string
numberOrString.toString()
numberOrString.valueOf()
numberOrString.toLocaleString()
function getLength(val: number | string): number {
// as 类型断言关键字,明确告知 TS 编译器,你没法判断我的代码,本人很清楚该变量的数据类型
const str = val as string
if(str.length){
return str.length
} else {
const num = val as number
return num.toString().length
}
}
/**
* 类型守卫
*/
function getLength01(val: number | string): number {
if(typeof val === 'string'){
return val.length
} else {
return val.toString().length
}
}
七、Class 类、类与接口
Class 类定义了一切事物的抽象特点
对象 Object 是类的实例
面向对象 OOP 三大特性:封装、继承、多态
✦ 封装:对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要知道细节,就能通过对外提供的接口来访问该对象
✦ 继承:子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
✦ 多态:由继承而产生了相关的不同的类,对同一个方法可以有不同的响应
类修饰符
✦ Public:修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public
✦ Private:修饰的属性或方法是私有的,不能在声明它的类的外部访问
✦ Protected:修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的
/**
* 类 Class:定义了一切事物的抽象特点
* 对象 Object 是类的实例
*
* 面向对象 OOP 三大特性:封装、继承、多态
* 封装:对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要知道细节,就能通过对外提供的接口来访问该对象
* 继承:子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
* 多态:由继承而产生了相关的不同的类,对同一个方法可以有不同的响应
*
* 类修饰符
* Public:修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public
* Private:修饰的属性或方法是私有的,不能在声明它的类的外部访问
* Protected:修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的
*/
class Animal {
name: string
constructor(name: string){
this.name = name
}
run () {
return `${this.name} is running`
}
}
const snake = new Animal('lily')
/**
* 类的继承
*/
class Dog extends Animal {
bark() {
return `${this.name} is barking`
}
}
const xiaobao = new Dog('xiaobao')
console.log(xiaobao.run())
console.log(xiaobao.bark())
/**
* 重写构造函数,注意在子类的构造函数中,必须使用 super 调用父类的方法,不然会报错
*/
class Cat extends Animal {
constructor(name){
super(name)
console.log(this.name)
}
run() {
return 'Meow, ' + super.run()
}
}
const maomao = new Cat('maomao')
console.log(maomao.run())
类和接口
类使用 implements 实现接口
实现多个接口,中间用逗号隔开
interface Radio {
switchRadio(trigger: boolean): void // 开关收音机
}
class Car implements Radio { // implements 实现
switchRadio(trigger) {
return 123
}
}
class Cellphone implements Radio {
switchRadio() {}
}
interface Battery { // Battery 电池
checkBatteryStatus(): void
}
// 实现多个接口,中间用逗号隔开即可
class Cellphones implements Radio, Battery {
switchRadio() {}
checkBatteryStatus() {}
}
八、泛型 Generics
泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而是在使用的时候再指定类型的一种特性
function echo(arg) {
return arg
}
const result = echo(123)
// 此时发现一个问题,传入的是数字,返回的是 any
function echo02<T>(arg: T): T {
return arg
}
const result02 = echo02(123)
// 泛型可以传入多个值
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]]
}
const result03 = swap(['string', 123])
泛型约束
函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性和方法
function echoWidthArr<T>(arg: T): T {
// console.log(arg.length)
/**
* 上面例子报错,泛型 T 不一定包含 length 属性,可以给他传入任意类型
* 有些不包含 length 属性,就会报错
*/
return arg
}
interface IWidthLength {
length: number
}
function echoWidthLength<T extends IWidthLength>(arg: T): T {
console.log(arg.length)
return arg
}
echoWidthLength('str')
const result3 = echoWidthLength({ length: 10 })
const result4 = echoWidthLength([1, 2, 3])
console.log(result3, result4)
泛型与类和接口
class Queue { // Queue 队列
private data = []
push(item) {
// push 把参数添加到数组项的尾部,返回的是新数组的长度
return this.data.push(item)
}
pop() {
// shift 删除数组第一项,返回的是被删除的值
return this.data.shift()
}
}
const queue = new Queue()
queue.push(1)
queue.push('str')
console.log(queue)
/**
* 上述代码存在一个问题,它允许你向队列中添加任何类型的数据
* 当数据被弹出队列时,也可以是任意类型
* 上面示例中,看起来人们可以向队列中添加 string 类型的数据
* 但在使用的过程中,会出现我们无法捕捉到的错误
*/
// 类中使用泛型
class Queue1<T> {
private data = []
push(item: T) {
return this.data.push(item)
}
pop(): T{
return this.data.shift()
}
}
const queue1 = new Queue1<number>()
// 泛型和 interface
interface KeyPair<T, U> { // KeyPair 键值对
key: T
value: U
}
let kp1: KeyPair<number, string> = {key: 1, value: 'str'}
let kp2: KeyPair<string, number> = {key: 'str', value: 1}
九、类型别名和交叉类型
类型别名,就是给类型起一个别名,让它可以更方便的被重用
let sum: (x: number, y: number) => number
const result = sum(1, 2)
type PlusType = (x: number, y: number) => number
let sum2: PlusType
// 支持联合类型
type StrOrNumber = string | number
let result2: StrOrNumber = '123'
result2 = 123
// 字符串字面量
type Directions = 'Up' | 'Right' | 'Bottom' | 'Left'
let toWhere: Directions = 'Up'
console.log(toWhere)
交叉类型 Intersection Types
interface IName {
name: string
}
type IPerson = IName & { age: number }
let person: IPerson = { name: 'hello', age: 12 }
十、声明文件
十一、内置类型
const a: Array<number> = [1, 2, 3]
/**
* 大家可以看到这个类型,不同的文件中有多处定义,但是它们都是内部定义的一部分
* 然后根据不同的版本或者功能合并在了一起,一个 interface 或者类多次定义会合并在一起
* 这些文件一般都是以 lib 开头,以 d.ts 结尾,告诉大家,我是一个内置对象类型
*/
const date: Date = new Date()
const reg = /abc/
/**
* 还可以使用一些 build in object 内置对象,比如 Math 与其他全局对象不同的是
* Math 不是一个构造器, Math 的所有属性与方法都是静态的
* Math.pow(x, y) x 的 y 次幂,x 是底数,y 是幂,x、y 必须是数字
* 如果结果是虚数或负数,该方法将返回 NaN。如果由于指数过大而引起浮点溢出,则该方法将返回 Infinity
*/
Math.pow(2, 2)
/**
* DOM 和 BOM 标准对象
* document 对象,返回的是一个 HTMLElement 对象
*/
let body: HTMLElement = document.body
// document 上面的 query 方法,返回的是一个 nodeList 类型
let allLis = document.querySelectorAll('li')
/**
* 添加事件也是很重要的一部分,document 上面有 addEventListener 方法,这是一个回调函数
* 因为类型推断,这里面的 e 事件对象也自动获得了类型,这里是个 mouseEvent 类型
* 因为点击是一个鼠标事件,现在我们可以方便的使用 e 上面的方法和属性
*/
document.addEventListener('click', e => {
e.preventDefault()
})
Typescript 还提供了一些功能性,帮助性的类型,这些类型,大家在 js 的世界是看不到的,这些类型叫做 utility types,提供一些简洁明快而且非常方便的功能
// partial,它可以把传入的类型都变成可选
interface IPerson {
name: string
age: number
}
let viking: IPerson = { name: 'vikong', age: 20 }
type IPartial = Partial<IPerson>
let viking2: IPartial = {}
// Omit,它返回的类型可以忽略传入类型的某个属性
type IOmit = Omit<IPerson, 'name'>
let viking3: IOmit = { age: 20 }