TypeScript笔记总结!!

209 阅读14分钟

TypeScript基本介绍

TS官方文档:www.typescriptlang.org/

中文文档(不再维护):www.tslang.cn/

TypeScript简称TS,是JavaScript的超集,简单来说就是:JS有的TS都有。

为什么要有typescript?

  • 背景:JS 的类型系统存在“先天缺陷”弱类型,JS 代码中绝大部分错误都是类型错误(Uncaught TypeError)
  • 这些经常出现的错误,导致了在使用 JS 进行项目开发时,增加了找 Bug、改 Bug 的时间,严重影响开发效率

原理分析

  • 从编程语言的动静来区分,TypeScript 属于静态类型的编程语言,JavaScript 属于动态类型的编程语言

    • 静态类型:编译期做类型检查(早)
    • 动态类型: 执行期做类型检查(晚)
  • 对于JS来说:需要等到代码真正去执行的时候才能发现错误

  • 对于TS来说:在代码编译的时候(代码执行前)就可以发现错误

安装编译TS的工具包

  • 因为node.js/浏览器。只认识JS代码,不认识TS代码
  • 需要先将TS代码转化为JS代码,然后才能运行

安装步骤

  1. 执行安装指令

    • npm i -g typescript
    • yarn global add typescript
    • 注意:Mac 电脑安装全局包时,需要添加 sudo 获取权限:sudo npm i -g typescript
    • yarn 全局安装:sudo yarn global add typescript

2.查看安装是否成功

  • tsc -v(查看typescript的版本)

类型注解

  let sum:number = 10;
  ​
  sum = 'hhh'  // 报错
  sum = 333    // 通过
  • 代码中的:number就是类型注解

  • 类型注解可以为变量添加类型约束

    • 一开始定义了什么类型,约定了以后只能给变量赋值该类型的值
    • 如果赋值了其他类型的数据,就会报错
  • 约定了类型之后,代码的提示就会非常的清晰,更便于后期找bug

类型概述

能够理解TypeScript中有哪些数据类型

说明

  • 了解类型注解,就必须了解ts中一共有哪些类型

  • TS中的常用基础类型细分为两类:

    • JS已有类型
    • TS新增类型

JS已有类型

  • 原始类型,简单类型(string、boolean、null、undefined、number)
  • 复杂数据类型(数组、对象、函数)

TS新增类型

  • 联合类型
  • 自定义类型(类型别名)
  • 接口
  • 元组
  • 字面量类型
  • void
  • 等等...

原始数据类型

  • number / string / boolean / null / undefined
  • 特点:简单,这些类型,完全按照 JS 中类型的名称来书写
  let age: number = 18
  let myName: string = '老师'
  let isLoading: boolean = false
  let obj: null = null
  let und: undefined = undefined
  // 注意点:
  //  null 类型的值只能是 null
  //  undefined 类型的值只能是 undefined

数组类型

数组类型的两种写法:

  // 写法一:
  let numbers: number[] = [1, 3, 5]
  // 写法二:
  let strings: Array<number> = [1, 3, 5]
  // 推荐使用 `number[]` 写法

联合类型

定义:

  • 如果定义的数据既可以是number类型,又可以是string类型
  • 可以使用联合类型

语法:

  let arr: (number | string)[] = [1, 'a', 3, 'b']
  • 解释:|(竖线)在 TS 中叫做联合类型,即:由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种。
  • 注意:这是 TS 中联合类型的语法,只有一根竖线,不要与 JS 中的或(|| 或)混淆了
  // 定义一个变量,既可以是 number 类型,又可以是 string 类型
  let foo: number | string = 123// 定义一个数组,数组中可以有数字或者字符串, 需要注意 | 的优先级
  let arr: (number | string)[] = [1, 'abc', 2]
  ​
  // 应用:定义一个定时器变量
  let timer: number | null = null
  timer = setInterval(() => {}, 1000)

类型别名

说明:

  • 类型别名(自定义类型):为任意类型起别名
  • 使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用
  let arr1 : (number | string )[] = [1,'a',2,'b']
  let arr2 : (number | string )[] = [5,'A',6,'B']
  ​
  // 利用类型别名
  type MyArray = (number | string)[]
  ​
  let arr1: MyArray = [1, 'a', 3, 'b']
  let arr2: MyArray = ['x', 'y', 6, 7]
  • 使用 type关键字来创建自定义类型
  • 类型别名(比如,此处的 MyArray)可以是任意合法的变量名称
  • 推荐使用大写字母开头
  • 创建类型别名后,直接使用该类型别名作为变量的类型注解即可

函数类型-基本使用

说明:

  • 函数的类型实际上指的是:

    • 函数参数 & 返回值的类型

单独指定参数、返回值的类型:

  // 函数声明
  function test(num1: number, num2: number): number {
    return num1 + num2
  }
  ​
  // 箭头函数
  const test = (num1: number, num2: number): number => {
    return num1 + num2
  }

函数类型-指定整个函数的类型

说明:

  • 函数也可以直接指定整个函数的类型
  • 应用:如果要封装计算方法(+,-,*,/)

指定整个函数的类型

  type Addfn = (num1:number , num2:number)=>numberconst add:Addfn= (num1,num2)=>{
  return  num1 + num2
  }
  • 当函数作为表达式时,可以通过类似箭头函数形式的语法来为函数添加类型
  • 注意:这种形式只适用于函数表达式

函数类型-void类型

说明

  • 如果函数没有返回值,那么函数的返回值类型为:void
  function test(name:string):void{
    console.log('hello',name)
    // 只依赖该函数的一些操作,而不依赖该函数的返回值
  }

注意:如果一个函数没有返回值,此时,在TS的类型中,应该使用void类型

  // 如果什么都不写,此时,add 函数的返回值类型为: void
  const add = () => {}
  // 这种写法是明确指定函数返回值类型为 void,与上面不指定返回值类型相同
  const add = (): void => {}
  ​
  // 但是,如果指定 返回值类型为 undefined,此时,函数体中必须显示的 return undefined 才可以
  const add = (): undefined => {
    // 此处,返回的 undefined 是 JS 中的一个值
    return undefined
  }

函数类型-可选参数

说明:

  • 使用函数实现某个功能时,参数也可以传也可以不传。
  • 这种情况下,在给函数参数指定类型时,就用到可选参数
  • 比如,数组的slice方法,可以slice()也可以slice(2)还可以slice(2,5)
  function mySlice(start?: number, end?: number): void {
    console.log('起始索引:', start, '结束索引:', end)
  }
  • 可选参数:在可传可不传的参数名称后面添加 ?(问号)
  • 注意:可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数

对象类型-基本使用

说明:

  • JS中的对象是由属性和方法构成的。
  • TS对象的类型就是描述对象的结构(有什么类型的属性以及方法)

语法:

  // 空对象
  let person: {} = {}
  ​
  // 有属性的对象
  let person: { name: string } = {
    name: '同学'
  }
  ​
  // 既有属性又有方法的对象
  // 在一行代码中指定对象的多个属性类型时,使用 `;`(分号)来分隔
  let person: { name: string; sayHi(): void } = {
    name: 'jack',
    sayHi() {}
  }
  ​
  // 对象中如果有多个类型,可以换行写:
  // 通过换行来分隔多个属性类型,可以去掉 `;`
  let person: {
    name: string
    sayHi(): void
  } = {
    name: 'jack',
    sayHi() {}
  }

注意

  • 使用{}来描述对象的结构
  • 属性采用属性名:类型的形式
  • 方法采用方法名():返回值类型的形式

对象类型-箭头函数参数类型

指定学生的类型

  • 属性:姓名、性别、成绩、身高
  • 方法:学习 打机

方法的类型也可以使用箭头函数形式

  {
      greet(name: string):string,
      greet: (name: string) => string
  }
  ​
  type Person = {
    greet: (name: string) => void
    greet(name: string):void
  }
  ​
  let person: Person = {
    greet(name) {
      console.log(name)
    }
  }

13、对象类型-对象可选属性

说明:

  • 对象的属性或方法,也可以是可选的,此时就用到可选属性
  • 比如,我们在使用 axios({ ... }) 时,如果发送 GET 请求,method 属性就可以省略
  • 可选属性的语法与函数可选参数的语法一致,都使用 ?来表示
  type Config = {
    url: string
    method?: string
  }
  ​
  function myAxios(config: Config) {
    console.log(config)
  }

对象类型-使用类型别名

定义两个学生对象

  • 属性:姓名 性别 成绩 身高
  • 方法:学习 打游戏
  // 创建类型别名
  type Person = {
     name:string,
     sayHi():void
  }
  ​
  // 使用类型别名作为对象的类型
  let person :Person = {
    name:'jack',
    say(){}
  }

接口类型-基本使用

说明:

  • 当一个对象类型被多次使用时,可以使用type来描述对象的类型,达到复用类型的目的
  • 也可以使用接口( interface )

接口

  1. 使用interface关键字来声明接口
  2. 接口名称(比如,此处的 IPerson),可以是任意合法的变量名称,推荐以I 开头
  3. 声明接口后,直接使用接口名称作为变量的类型
  4. 因为每一行只有一个属性类型,因此,属性类型后没有 ;(分号)
  interface IPerson {
    name:strng,
    age:number,
    sayHi():void
  }
  ​
  const obj : IPerson = {
    name: 'jack',
    age: 19,
    sayHi() {}
  }
  ​

接口类型--- interface vs type

interface(接口) 和 type (类型别名)的对比

  • 相同点:都可以给对象指定类型

  • 不同点:interface(接口):只能为对象指定类型

  • type(类型别名):

    • 不仅可以为对象指定类型
    • 还可以为任意类型指定别名
  • 推荐:能使用type就是用type

  interface IPerson {
    name: string
    age: number
    sayHi(): void
  }
  ​
  // 为对象类型创建类型别名
  type IPerson = {
    name: string
    age: number
    sayHi(): void
  }
  ​
  // 为联合类型创建类型别名
  type NumStr = number | string

接口类型-接口继承

作用:

  • 如果两个接口之间如果由相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用。
  • 比如:这两个接口都有x、y两个属性,重复写两次,可以,但很繁琐。
  interface Point2D{
     x:number
     y:number
  }
  interface Point3D{
    x:number
    y:number
    z:number
  }
  • 利用继承
  interface Point2D{
     x:number
     y:number
  }
  // 继承Point2D接口类型
  interface Point3D extends Point2D{
     z:number
  }

总结:

  1. 使用extends( 继承 ) 关键字实现了接口Point3D继承了Point2D

  2. 继承后Point3D就有了 Point2D 的所有属性和方法

    • 这个时候,Point3D 同时有 x、y、z 三个属性

元组类型

  • 在地图中,使用经纬度坐标来标记位置信息
  • 可以使用数组来记录坐标,那么,该数组中只有两个元素,并且这两个元素都是数值类型
  let position:number[] = [111.20,33,55]
  • 使用number[]的缺点:不严谨的,因为该类型的数组中可以出现任意多个数字
  • 更好的方法:元组 Tuple
  • 元组类型是另一种类型的数组,它确切地知道包含多少个元素,以及特定索引对应的类型
  let position: [number, number] = [39.5427, 116.2317]

注意:

  • 元组类型可以确切地标记出有多少个元素,以及每个元素的类型
  • 该示例中,元组有两个元素,每个元素的类型都是number

类型推论

说明:

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

  • 换句话说:由于类型推论的存在,这些地方,类型注解可以省略不写

    • 声明变量并初始化时
    • 决定函数返回值
  // 变量 age 的类型被自动推断为:number
  let age = 18// 该函数返回值的类型被自动推断为:number
  function add(num1: number, num2: number) {
    return num1 + num2
  }

注意

  • 省略类型注解的地方就省略偷懒,充分利用TS类型推论的能力hhhh,提高开发效率)
  • 技巧:如果不知道类型,可以通过鼠标放在变量名称上,利用VSCode的提示来查看类型

字面量类型-基本使用

  let str1 = 'Hello TS'    // ===> 推断为string类型
  const str2 = 'Hello TS'  // ===> 推断为 Hello TS类型

这里为什么str1 和 str2 不都是 string类型呢?

str1是一个变量(let),它的值可以是任意字符串的,所以类型为:string

str2是一个变量 (const),它的值是不能变化的,只能是'Hello TS',所以,它的类型为:'Hello TS'

  • 注意:此处的'Hello TS',就是一个字面量类型,也就是说某个特定的字符串也可以作为TS中的类型

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

    • 字面量:
       {name:'yang'} [] 18 20  'abc' false function () {}
    

字面量类型-使用模式和场景

说明

  • 使用模式:字面量类型配合类型一起使用
  • 使用场景::用来表示一组明确的可选值列表

比如:

  • 性别: 男 / 女
  • axios的请求方式: get / post / put / delete / patch
  // 使用自定义类型:
  type Direction = 'get' | 'post' | 'delete' | 'patch'
  function axios (method:Direction){
    console.log(method)
  }
  ​
  // 调用函数时,会有类型提示
  axios('post')

结论:

  • 相对于string类型,使用字面量类型更加精确、严谨

any类型

说明:

  • 原则:不推荐使用any

    • 这会让TypeScript变为AnyScript(失去TS类型保护的优势)

    • 因为当值的类型为any时,可以对该值进行任意操作,并且不会有代码提示

        
        let obj: any = { x: 0 }
        ​
        obj.bar = 100
        obj()
        const n: number = obj
      

    注意

    • 以上操作都不会有任意类型错误提示,即使可能存在错误

    • 这样就失去了TS给我们带来的类型判断作用了

    • 尽可能的避免使用 any类型:以下这两种情况此外

      • 除非临时使用any来避免书写很长、很复杂的类型
      • 使用时不关注数据的类型(如:console.log())
    • 其他隐式具有any类型的情况

      • 声明变量不提供类型也不提供默认值
      • 函数参数不加类型
    • 注意: 因为不推荐使用any,所以,这两种情况下都应该提供类型

类型断言-基本使用

  • 定义一个变量,默认存放空对象
  • 对象中的属性来源于网络请求
  • 使用对象中的属性

有时候你会比 TS 更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型。

  type Use = {
    name:string
    age:sting
    gender:sting
  }
  // 指定类型之后,直接给use赋值空对象会报错
  // 但是我们是明确知道 use对象就是Use类型
  // 可以通过 as 来将空对象转为use,以达到显示提示的目的
  let use:Use = {} as Use// 写法二
  let use:Use = <Use> {}
  setTimeout(() => {
    use = {
      name: 'zs',
      age: 18,
      gender: '男'
    }
  }, 1000);
  console.log(use.name)

类型断言-操作dom

  • 类型断言还在dom操作时用的比较多
  // 直接获取 dom 之后,无法点出 link 中的属性
  const aLink = document.getElementById('link')
  ​
  // 获取 dom 之后,将 link 断言为HTMLAnchorElement,可以轻松点出属性
  const aLink = document.getElementByid('link') as HTMLAnchorElement

泛型-泛型函数

定义一个函数:

  • 函数的参数为Number类型时,函数的返回值也为Number类型
  • 函数的参数为 String 类型时,函数的返回值也为 String 类型
  function fn<Type>(value:Type) :Type {
    return value
  }
  ​
  const res1 = fn<Number>(1)
  const res2 = fn<String>('abc')
  ​
  // 简化:调用时可以省略类型
  const res1 = fn(1)
  const res2 = fn('abc')

说明:

  • :就是泛型。
  • 泛型:是可以在保证类型安全前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class 中。
  • ”Type“:只是一个类型的占位,可以使用其他名称来替换。如:”T“
  • 在调用泛型函数时,可以省略<类型>来简化泛型函数的调用

注意

  1. 语法:在函数名称的后面添加<>,尖括号中添加类型变量,比如此处的Type
  2. 类型变量Type,是一种特殊类型的变量,它处理类型而不是值
  3. 该类型变量相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时指定)
  4. 因为Type是类型,因此可以将其作为函数参数和返回值的类型,表示参数和返回值具有想用的类型
  5. 类型变量Type,可以是任意合法的变量名称
  6. 可以在调用泛型函数时,省略类型参数

泛型-泛型约束

  • 定义一个函数

    • 函数的参数为某一类型时,函数的返回值也为该类型
    • 参数中的要有一个属性:length
  interface ILenth {
    length:number
  }
  ​
  function fn<T extends ILength> (value :T) :T {
    console.log(value.length)
    return value
  }
  ​
  // const res1 = fn<Number>(1)    // 报错
  const res2 = fn('abc')
  const res3 = fn([1, 2, 3])
  const res4 = fn({
    length: 11
  })

说明

  • 默认情况下,泛型函数的类型变量Type可以代表多个类型,这导致无法访问任何属性
  • 可以使用泛型约束,给传入的泛型添加限制条件

泛型 - 泛型接口

  • 泛型接口:接口也可以配合泛型来使用,以增加其灵活性,增强其复用性
  interface IUse<Type> {
    name : Type
    age: number
    gender: Type
    sayHi():Type
  }
  ​
  let obj : IUse <string> = {
    name:'zs',
    age:18,
    gender:'男',
    sayHi(){
      return this.name
  }
  }

注意:

  • 在接口名称的后面添加<类型变量>,那么,这个接口就变成了泛型接口。
  • 接口的类型变量,对接口中所有其他成员可见,也就是接口中所有成员都可以使用类型变量
  • 使用泛型接口时,需要显式指定具体的类型