Deng
Deng
基于TypeScript从零重构axios | odjBlog
    欢迎来到odjBlog的博客!

基于TypeScript从零重构axios

web前端学习 odjbin 3个月前 (09-07) 52次浏览 0个评论

为什么要学习 TypeScript

  • 是 JavaScript 的增强
  • 添加可选择类型标注, 大大增强了代码的可读性和可维护性
  • 提供最新和不断发展 JavaScript 特性, 能让我们建立更兼容的组件
  • 未来前端开发趋势
  • 技术转型的趋势
  • 提升个人能力和竞争力

安装 typescript

  • npm install -g typescript

基础类型

//index.ts
//数字
let decLiteral: number = 20;
let hexLiteral: number = 0x14;
let binaryLiteral: number = 0b10100
let octalLiteral: number = 0o24

//字符串
let name1: string = 'fd'
let age:number=20
let sentence=`hello my name is ${name1}`//用这种
// let sentence='hello my name is'+name

//数组
let list:number[]=[1,2,3]//推荐第一种
// let list1:Array<number>=[1,2,3]

let x:[string,number]
x=['hello',10]
console.log(x[0].substr(1))

//元组
let y:[string,number]
y=['eee',10]
console.log(y[0].toString())

//enum 枚举类型
enum Color{
  Red,
  Green,
  Blue
}
let c:Color=Color.Green

//any
let notSure:any=4
notSure='maybe a'
notSure=false

//void
function warnUser():void{
  console.log('this is warning message')
}
//null undefined
let u: undefined=undefined
let n:null=null //undefined
let num:number|null=3
num=null

//never
function error(msg:string):never{
  throw new Error(msg)
}
function fail(){
  return error('something failed')
}
//无限循环
function iinj():never{
  while (true){

  }
}
//object
declare function create(o:object|null):void;
//非原始类型
create({prop:0})
create(null)

//原始类型
create(42)
create('string')
create(false)
create(undefined)

//类型断言
let someValue:any='a string'
//let strLength:number=(<string>someValue).length
let strLength1:number=(someValue as string).length //推荐

变量声明

var 声明

//index.js
//非常典型的闭包场景
// function f(){
//     var a=10
//     return function g(){
//         var b=a+1
//         return b
//     }
// }
//
// var g=f()
// console.log(g())//11

//var 的作用域规则
// function f(should){
//     if(should){
//         var x=10
//     }
//     return x
// }
// f(true)//10
// f(false)//undefined

function sumMatrix(matrix){
    var sum=0
    for (var i=0;i<matrix.length;i++){
        var currentRow=matrix[i]
        for (var j = 0; j < currentRow.length; j++) {
            sum+=currentRow[j]
        }
    }
    return sum
}
var matrix=[
    [1,2,3],
    [4,5,6]
]
console.log(sumMatrix(matrix))//21

//面试经常考的
for (let i = 0; i < 10; i++) {
    (function (j){
        setTimeout(function (){
            console.log(j)
        },100*j)//0-9
    })(i)
    // setTimeout(function (){
    //     console.log(i)
    // },100*i)//视频上是 10 个 10, 但是我运行是 0-9
}

let 声明

/* (1)块级作用域
function f(input: boolean) {
  let a = 100
  if (input) {//块级作用域
    let b = a + 1 //a 内部可以访问外部
    return b
  }
  return b //不存在
}
*/
/* (2)
try{
  throw 'Oh no'
}catch (e){
  console.log('catch')
}
console.log(e)//访问不到
 */

/* (3)在声明之前不能被读写的 ->暂时性死区
a++
let a=1
*/

/* 暂时性死区
function foo(){
  return a
}
foo()
let a
*/

//let 变量不能被重复声明 (在块级可以)
/*
function f(condition, x) {
  if (condition) {
    let x = 100
    return x
  }
  return x
}

f(false, 0)
f(true, 0)
*/

function sumMatrix(matrix: number[][]) {
  let sum = 0
  for (let i = 0; i < matrix.length; i++) {
    let currentRow = matrix[i]
    for (let j = 0; j < currentRow.length; j++) {
      sum += currentRow[j]
    }
  }
  return sum
}
let matrix=[
  [1,2,3],
  [4,5,6]
]
console.log(sumMatrix(matrix))//21

//块级作用域的获取
for (let i = 0; i < 10; i++) {
  setTimeout(function (){
    console.log(i)//10 次 10 setTimeout 是异步执行的
  },100*i)
}

解构

//数组解构
let input:[number,number] =[1,2]
function f([first,second]:[number,number]){
  console.log(first)//1
  console.log(second)//2
}
f(input)

let [first,...rest]=[1,2,3,4]
console.log(first)//1
console.log(rest)//[ 2, 3, 4 ]

let [,second,,fourth]=[1,2,3,4]
console.log(second)//2
console.log(fourth)//4

//对象解构
let o={
  a:'foo',
  b:12,
  c:'bar'
}
let {a,...passthrough}=o
let total=passthrough.b+passthrough.c.length
console.log(total)//15

// let {a,b}:{a:string,b:number}=o

//默认值的情况
function keepWholeObject(wholeObject:{a:string,b?:number}){
  let {a,b=1001}=wholeObject
}
//函数声明
type C={a:string,b:number}
function f1({a,b=0}={a:''}):void{

}
f1({a:'yes'})
f()

展开

//数组展开 ...args  浅拷贝

//对象展开
let defaults={
  food:'spicy',
  price:'$10'
}
let search={...defaults,food: 'rich'}
console.log(search)//{ food: 'rich', price: '$10' }

接口

可选属性+只读属性+额外属性检查

interface LabelledValue{
  label:string
}
//类型检测器
function printLabel(labelledObj:LabelledValue){
  console.log(labelledObj.label)
}
let myObj={size:10,label:'Size 10 Object'}
printLabel(myObj)//Size 10 Object

//可选属性
interface Square{
  color:string
  area:number
}
interface SquareConfig{
  color?:string
  width?:number
  //(2)
  [propName: string]: any
}
function createSquare(config: SquareConfig):Square{
  let newSquare={color:'white',area:100}
  if(config.color){
    newSquare.color=config.color
  }
  if(config.width){
    newSquare.area=config.width*config.width
  }
  return newSquare
}
//ts 会对对象字面量, 做额外的属性检查,一旦发现所传的对象有些属性不在定义的属性里面,就会报错
//额外属性检查, 如果 colour 这属性,是真正 bug 的来源, 要去检查 SquareConfig
//(1)一种通过类型断言,  这种并不好
// let squareOptions={colour:'black',width:100} as SquareConfig
//(2)
//let squareOptions = {color: 'black', width: 100}
//let mySquare = createSquare(squareOptions)
let mySquare = createSquare( {color: 'black', width: 100})
//只读属性
interface Point{
  readonly x:number
  readonly y:number
}
let p1:Point={x:10,y:20}
p1.x=5

let a:number[]=[1,2,3,4]
let ro:ReadonlyArray<number>=a
// ro[0]=12
a=ro as number[]

函数类型+可索引的类型

//函数类型
interface SearchFunc{
  (source:string,substring:string):boolean
}
let mySearch:SearchFunc
mySearch=function (src,sub):boolean{
  let result=src.search(sub)//result 是 number 类型
  return result>-1//变为 boolean 类型
}

//可索引的类型, 数字索引签名
interface StringArray{
  [index:number]:string
}
let myArray:StringArray
myArray=['bob','fred']

let myStr:string=myArray[0]

//字符串索引签名
class Animal{
  name:string
}
class Dog extends Animal{
  breed:string
}
interface NotOkay{
  [x:number]:Dog
  [x:string]:Animal
}

//
interface NumberDictionary{
  [index:string]:number
  length:number
  // name:string 会报错
}
//只读
interface ReadonlyStringArray{
  readonly [index:number]:string
}
let myArray:ReadonlyStringArray=['Alice','Bob']
myArray[2]='Mallory'

类类型+继承接口+混合类型+接口继承类

  • 类类型
//类类型
interface ClockInterface {
  currentTime: Date;
  setTime(d:Date)
}
//构造器类型
interface ClockConstructor {
    new (hour: number, minute: number);
}

//类实现接口
class Clock implements ClockInterface {
    currentTime: Date;
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) { }
}
//实例类型
interface ClockInterface {
    tick()
}
interface ClockConstructor {
    new (hour: number, minute: number): ClockInterface;
}
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
    return new ctor(hour, minute);
}
class DigitalClock implements ClockInterface {
    constructor(h:number,m:number) {
    }
    tick() {
        console.log("beep beep")
    }
}

class AnalogClock implements ClockInterface {
    constructor(h:number,m:number) {
    }
    tick() {
        console.log("tick tock")
    }
}
  • 继承接口
interface Shape{
    color:string
}
interface PenStroke{
    penWidth:number
}
interface Square extends Shape,PenStroke{
    sideLength:number
}
let squre={} as Square
squre.color='blue'
squre.sideLength=10
squre.penWidth=5.0
  • 混合类型
interface Counter{
    (start:number):string
    interval:number
    reset():void
}
function getCounter():Counter{
    let couter=(function (start:number){

    })as Counter
    couter.interval=123
    couter.reset=function (){}
    return couter
}
let c=getCounter()
c(10)
c.reset()
c.interval=5.0
  • 接口继承类
class Control{
    private state:any
}
interface  SelectableControl extends Control{
    select()
}
class Button extends Control implements SelectableControl{
    select() {
    }
}
class TextBox extends Control{
    select(){

    }
}
class ImageC implements SelectableControl{
    select() {
    }
}

基本示例+继承

class Greeter{
    greeting: string;
    constructor(message: string){
        this.greeting = message;
    }
    greet(){
        return "Hello, " + this.greeting;
    }
}
let greeter=new Greeter('world')
greeter.greet()
  • 继承
class Animal {
    name: string
    constructor(name:string) {
      this.name=name
    }
    move(distance: number = 0) {
        console.log(`${this.name} moved ${distance}m`)
    }
}
class Snake extends Animal {
    constructor(name: string) {
        super(name)
    }
    move(distance: number = 5) {
        console.log("Slithering...")

    }
 }

 class Horse extends Animal{
    constructor(name:string) {
        super(name);
    }
    move(distance: number = 45) {
        console.log("Galloping...")
        super.move(distance)
    }
 }
 let sam=new Snake('Sammy')
let tom:Animal=new Horse('Tommy')
sam.move()
tom.move(34)

公共,私有与受保护修饰符+readonly 修饰符

  • 受保护修饰符 只能在内部和子类中使用

存取器+静态属性?

  • 支持 get set
let passcode="secret passcode"
class Emplyee{
    private _fullName:string
    get fullName():string{
        return this._fullName
    }
    set fullName(newName:string){
        if(passcode&&passcode==='secret passcode'){
            this._fullName=newName
        }else {
            console.log('Error : unauthorized update')
        }
    }
}
let employee=new Emplyee()
employee.fullName='Bob Smith'
if(employee.fullName){
    console.log(employee.fullName)
}

抽象类

abstract class Department{
    name:string
    constructor(name:string) {
        this.name=name
    }
    printName():void{
        console.log('Dept name'+this.name)
    }
    abstract printMeeting():void
}

class AccountingDepart extends Department{
    constructor() {
        super('Accounting')
    }
    printMeeting(): void {
        console.log('The Accounting Department meets each Monday at 10am.')
    }
    generateReports(): void {
        console.log('Generating accounting reports...')
    }
}
//抽象类不能被实例化,只能被继承
//派生类必须实现抽象类中的抽象方法
let department:AccountingDepart
department=new AccountingDepart()
department.generateReports()

let dept:Department
dept=new AccountingDepart()
dept.printMeeting()
dept.printName()

函数

泛型

/**
 * identity 函数是一个泛型函数,用于返回传入的数组本身。
 * 它的主要作用是演示泛型的使用方法,以及在函数内部如何处理泛型数组。
 * 
 * @param arg {T[]} - 一个泛型数组,可以包含任何类型的元素。
 * @returns {T[]} - 返回传入的数组,不做任何修改。
 */
function identity<T>(arg:T[]):T[] {
  // 输出数组长度,用于演示在函数内部可以访问数组的属性
  console.log(arg.length);
  // 返回传入的数组,不做任何修改
  return arg;
}

泛型类

// 定义泛型接口,用于约束实现该接口的函数必须是“输入和输出类型相同的函数”
interface GenericIdentityFn {
  <T>(arg: T): T;
}

// 声明一个变量 myIdentity,其类型为 GenericIdentityFn,并指定其具体类型参数为 number
// 这里假设 identity 函数是已经存在的,且符合 GenericIdentityFn 接口的定义
let myIdentity: GenericIdentityFn<number> = identity();

/**
 * 定义一个泛型类 GenericNumber,用于处理泛型数值或符合数值操作的类型
 * @template T 表示泛型参数,允许在实例化时指定具体的类型
 * 类包括静态部分和实例部分, 泛型类是指实例部分的类型, 静态属性不属于实例,所以不需要指定泛型参数
 */
class GenericNumber<T> {
  // 定义泛型类型的属性 zeroValue,表示零值
  zeroValue: T;
  // 定义泛型类型的 add 方法,接受两个参数并返回它们相加的结果
  add: (x: T, y: T) => T;
}

// 创建一个 GenericNumber 类的实例,指定类型参数为 number
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
// 设置数字类型的 add 方法为两个数字相加
myGenericNumber.add = function (x, y) {
  return x + y;
};

let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = '';
// 设置字符串类型的 add 方法为两个字符串拼接
stringNumeric.add = function (x, y) {
  return x + y;
};
console.log(stringNumeric.add(stringNumeric.zeroValue, 'test'));

处理请求 url 参数

  • url.ts
import { isDate, isObject, isPlainObject } from './util'
function encode(val:string):string{
  return encodeURIComponent(val)
    .replace(/%40/g,'@')
    .replace(/%3A/g,':')
    .replace(/%24/g,'$')
    .replace(/%2C/g,',')
    .replace(/%20/g,'+')
    .replace(/%5B/g,'[')
    .replace(/%5D/g,']')
}
export function buildURL(url: string, params?: any): string {
  if (!params){
    return url
  }
  const parts:string[]=[]
  // forEach return 是跳到下次循环
  Object.keys(params).forEach((key)=>{
    const val=params[key]
    if(val===null||typeof val==='undefined'){
      return
    }
    let values=[]
    if(Array.isArray(val)){
      values=val
      key+='[]'
    }else{
      values=[val]
    }
    values.forEach((val)=>{
      if(isDate(val)){
        val=val.toISOString()
      }else if(isPlainObject(val)){
        val=JSON.stringify(val)
      }
      parts.push(`${encode(key)}=${encode(val)}`)
    })
  })
  let serializedParams=parts.join('&')
  if(serializedParams){
    const marIndex=url.indexOf('#')
    if(marIndex!==-1){
      url=url.slice(0,marIndex)
    }
    url+=(url.indexOf('?')===-1?'?':'&')+serializedParams
  }
  return url
}

处理请求 body 参数

  • data.ts
import { isPlainObject } from './util'

export function transformRequest(data: any): any {
  if (isPlainObject(data)) {
    return JSON.stringify(data)
  }
  return data
}

处理请求 header

  • headers.ts
import { isPlainObject } from './util'

function normalizeHeaderName(headers:any,normalizedName:string):void{
  if(!headers) {
    return
  }
  Object.keys(headers).forEach((name)=>{
    if(name!==normalizedName&&name.toUpperCase()===normalizedName.toUpperCase()){
      headers[normalizedName]=headers[name]
      delete headers[name]
    }
  })
}
export function processHeaders(headers:any,data:any):any{
  normalizeHeaderName(headers,'Content-Type')
  if(isPlainObject(data)){
    if(headers&&!headers['Content-Type']){
      headers['Content-Type']='application/json;charset=utf-8'
    }
  }
  return headers
}

处理响应数据 , header, data

  • headers.ts
export function parseHeaders(headers:string):any{
  let parsed=Object.create(null)
  if(!headers){
    return parsed
  }
  headers.split('\r\n').forEach((line)=>{
    let [key,val]=line.split(':')
    key=key.trim().toLowerCase()
    if(!key){
      return
    }
    if(val){
      val=val.trim()
    }
    parsed[key]=val
  })
  return parsed
}

异常情况处理 error.ts

接口扩展( 工厂模式的运用 )

拦截器实现

合并配置的设计与实现

喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
已稳定运行:3年255天2小时11分