【源码共读】第2期 | 从vue3 工具函数中学习ts类型最佳实践

前言

学习目标

  • 学习ts在工具函数中的使用场景、作用及意义

util工具函数知识点

emptyObject中学Record

不可增、删、改的空对象及其原型,但是当对象嵌套过深还是能修改的

export const emptyObject: Record<string, any> = Object.freeze({})
/**
 * Record<string, any>:
 * 关于Record不得不说的故事
 * Record的来历:
 * Record是ts的内置类型,泛型类型
 * type Record<K extends keyof any, T> = {
 *  [P in K]: T
 * }
 * 定义了一个emptyObject变量,允许key为string类型,value为任意类型
 */

isUndef中is的使用

export function isUndef(v: any): v is undefined | null {
  return v === undefined || v === null
}


/**


 * v is undefined | null 如何理解呢?
 * 是ts中的类型谓词语法(type predicate):表示一个类型断言的结果
 * 语法形式: parameterName is Type
 * 解释: parameterName:参数名 Type是ts类型
 * v is undefined | null 理解为v是否属于undefined | null ,即返回的结果是一个Boolean类型
 * 作用:用于表示类型缩小操作,帮助ts编译器更准确推断变量类型,提高代码安全性
 */

isFunction中学习函数类型定义

export function isFunction(value: any): value is (...args: any[]) => any {
  return typeof value === 'function'
}


/**


 * value is (...args: any[]) => any 如何理解?
 * 表示断言value是函数类型,约束了value的类型范围
 * 函数返回值是一个Boolean类型,表示参数value是否属于(...args: any[]) => any 类型
 * (...args: any[]) => any 作为一个整体去理解,是一个函数类型,表示具有任意参数和任意返回值的函数类型
 * 可以将参数value的类型缩小为函数类型,入参中限制参数必须为函数类型
 * is这里还是用到了类型谓词语法,参照上述的isUndef
 * value is (...args: any[]) => any通过is类型断言为Boolean类型,fucntion中return typeof value === 'function'也是返回Boolean类型,形成一致约束
 *
 */

isRegExp中学习ts类型分类

export function isRegExp(v: any): v is RegExp {
  return _toString.call(v) === '[object RegExp]'
}


/**


 * v is RegExp 也是类型谓词语法形式,返回布尔类型,断言v是RegExp(正则)类型
 * 除此之外,延申一点:
 * ts中的类型可以分为以下几类:
 *  基本类型:string、number、boolean、null、undefined、symbol等都js的基本类型
 *  对象类型:Object、Date、RegExp、Array、class、tuple、enum等
 *  函数类型:普通函数类型、箭头函数类型、构造函数类型
 *  泛型:泛型函数类型、泛型类类型
 *  高级类型:交叉类型、联合类类型、类型别名、类型推断、类型守卫
 *  其他类型:void、never、unkown、any等
 *

 */

isPromise中学Promise类型定义

export function isPromise(val: any): val is Promise<any> {
  return (
    isDef(val) &&
    typeof val.then === 'function' &&
    typeof val.catch === 'function'
  )
}
/**
 * promise在ts类型定义中表示 interface Promise<T>{}
 * <T>表示泛型参数
 * 根据上述中对ts类型的总结可知 Promise<any>是一个泛型类型
 * Promise<any>表示返回值为任意类型的Promise对象
 */

extend中学习PropertyKey

export function extend(
  to: Record<PropertyKey, any>,
  _from?: Record<PropertyKey, any>
): Record<PropertyKey, any> {
  for (const key in _from) {
    to[key] = _from[key]
  }

  return to
} 
/**

 * PropertyKey:ts内置类型
 * type PropertKey = string | number | symbol
 * 作用:对于对象类型的key,用来约束其类型
 *

 * 使用场景:
 * 1.定义对象的属性名或方法名的类型
 * 2.定义函数的参数类型
 *
 * Record<PropertyKey, any>
 * 语法:Record<Keys, Values>
 * Keys 表示属性名的类型  Values 表示属性值的类型
 * 理解:约束了对象的属性名为number、string、symbol类型,属性值可以是任何类型
 */

looseIndexOf中学习unknown使用场景

export function looseIndexOf(arr: Array<unknown>, val: unknown): number {
  for (let i = 0; i < arr.length; i++) {
    if (looseEqual(arr[i], val)) return i
  }
  return -1
}
/**
 * ts中什么情况下需要使用unknown而不是any表示参数类型定义?
 * 关于unknown和any不得不区分的使用场景?
 *  unknown:变量的类型是未知的,需要进行类型检查或类型转换才能安全使用
 *  any:任何类型都可以赋值给变量
 * 使用场景(使用unknown而不是any):
 *  1.当函数参数类型未知时:参数类型未知,在函数内部对参数进行类型检查或类型转换操作,可用unknown表示
 *  2.当使用第三方库或接口时:这时候可能第三方库或接口返回的信息不完整或不准确就用unknown去定义,并在函数内部进行类型检查或类型转换
 *  any类型的使用会导致ts提供的类型检查和类型推断功能不执行
 * 故实际开发中优先使用更具体的类型来表示变量的类型
 */

once学习类型拆解

export function once<T extends (...args: any[]) => any>(fn: T): T {
  let called = false
  return function () {
    if (!called) {
      called = true
      fn.apply(this, arguments as any)
    }
  } as any
}
/**

 * function once<T extends (...args: any[]) => any>(fn: T): T {}
 * ts类型拆解:
 * 1.泛型函数
 * function once<T>(fn:T): T{}
 * 2.对声明的泛型类型T的约束
 * T extends (...args: any[]) => any :表示T约束为一个函数类型
 * (...args: any[]) => any 表示一个参数是任意,返回值任意的泛型函数类型
 * extends表示继承,约束,约束T为一个函数类型
 *
 */

hasChanged中学习as使用

export function hasChanged(x: unknown, y: unknown): boolean {
  if (x === y) {  
    // 为了处理x,y都是0或-0的边缘情况
    return x === 0 && 1 / x !== 1 / (y as number) // y as number 将y强制转化为number类型
  } else {
    return x === x || y === y // 为了处理x或y都是NaN(不是数字的情况)
  }

}
/**
 * as
 * 定义语法: value as Type
 * value:要进行类型转换的值,Type表示要转换成的类型
 *
 * 使用as的条件:
 * 1.要转换的值和目标类型必须兼容,即要转换的值必须包含目标类型的所有属性和方法,或者可以通过一些方法进行转换
 * 2.转换后的值的类型必须能够被ts推导出来,并且与目标类型相同或兼容
 *
 * 常用场景:
 * 1.将一种类型转为另一种类型
 * 2.将泛型类型转化为具体类型
 * 3.将断言类型转换为具体类型
 * 
*/

总结

通过对ts版本工具函数的学习,了解到了ts在实际开发场景中的实践,其中对Recordisas(...args: any[]) => anyts类型分类unknown与anyPropertyKeyPromise类型定义复杂ts拆解分析等有有了新的认识,也为自己在项目开发中如何使用ts提供了新的思路,从现在开始也尝试在自己项目中使用起来,上述所有见解都属于个人看法,有什么错误欢迎大家来指正!

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYjYkqby' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片