泛型函数、交叉类型

150 阅读6分钟

泛型函数写法

  1. 编写字符串排序、整数排序函数代码:
// 快速排序算法思路:
// 1.先从数列中取出一个数作为基准数。
// 2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
// 3.再对左右区间重复第二步,直到各区间只有一个数。

// 如果不用泛型,之后打点调用就没有提示
function quickSort(arr: Array<any>): any {
  if (arr.length < 2) { return arr }

  var left: Array<any> = [];
  var right: Array<any> = [];
  var mid = arr.splice(Math.floor(arr.length / 2), 1)[0];
  
  for (var i = 0; i < arr.length; i++) {
    if (arr[i] < mid) {
      left.push(arr[i]);
    } else {
      right.push(arr[i])
    }
  }
  return quickSort(left).concat(mid, quickSort(right))
}
  1. 泛型函数实现字符串排序,整数排序
function quickSort<T>(arr: Array<T>): Array<T> {
  if (arr.length < 2) { return arr }

  var left: Array<T> = [];
  var right: Array<T> = [];
  var mid = arr.splice(Math.floor(arr.length / 2), 1)[0];
  console.log("mid:", mid)
  for (var i = 0; i < arr.length; i++) {
    if (arr[i] < mid) {
      left.push(arr[i]);
    } else {
      right.push(arr[i])
    }
  }
  return quickSort(left).concat(mid, quickSort(right))
}

泛型函数重载

  1. 中文排序
function sortChinese<T>(arr: Array<T>): T[] {//Array<T>=T[]
  return arr.sort(function (firstnum, secondnum) {
    return (firstnum as any).localeCompare(secondnum, "zh-CN")
  })
}
  1. 字符串自排序
function strSelfSort(str: string): string {
  // (1) 字符串拆分成数组
  let strArray = str.split('');
  // (2) 数组进行使用快速排序算法来排序
  let strSortArray = quickSort(strArray);
  // (3) 重新把排好序的数组连接成一个字符串返回
  return strSortArray.join('');
}
  1. 中文+英文、数字数组 + 数组内部字符串自排序
// 判断数组中是否有中文元素
function isChinese<T>(arr: Array<T>): boolean {
  var pattern1 = /[\u4e00-\u9fa5]+/g;
  return arr.some((item) => {
    return pattern1.test(item as any)
  })
}

// 中文+英文、数字数组排序混合方法  
function sort<T>(arr: Array<T>): T[] {
  if (isChinese(arr)) {//如果是中文数组
    return sortChinese(arr);
  }
  let newArr = arr.map((item) => {
    return typeof item === "string" ? strSelfSort(item) : item
  })
  //英文、数字数组排序
  return quickSort(newArr as any);
}

export { }
  1. 字符串自排序 +中文+英文、数字数组 + 数组内部字符串自排序
// 中文+英文、数字数组排序混合方法  
function sort<T>(data: T, count: number = 5): T[] | string {
  if (typeof data === "string") {//如果是字符串
    return strSelfSort(data, count)// 按照字符串自排序
  }
  if (data instanceof Array) {//如果data是数组
    if (isChinese(data)) {//如果是中文数组
      return sortChinese(data);
    }
    let newArr = data.map((item) => {
      return typeof item === "string" ? strSelfSort(item) : item
    })
    //英文、数字数组排序
    return quickSort(newArr as any);
  }
}
var str = "bdfaerafdfsd"
let strResult = sort(str, 6) as string
console.log("长度为:", strResult.length, "字符串", strResult)

var numArr = [3, 1.883332, 8, 9, 20, 15, 2, 7, 13, 11, 19, 18, 5, 6, 17, 4];
// 注意,此时sort(numArr)返回的类型是string | number[][],根本不符合要求
// 不用函数重载全乱了!! 
console.log(sort(numArr));

let strArr: Array<string> = ["cba", "kkdf", "ndf", "bcdf", "dfd", "cdf"]
console.log(sort(strArr));

var chineseArr = ["武汉", "郑州", "太原", "济南", "沈阳", "大连"];
console.log(sort(chineseArr));

export { }
  1. 用泛型函数重载解决 5 的问题
// 泛型函数重载
// 中文+英文、数字数组排序混合方法  
// 分工明确
//function sort(data: string, count?: number): string[可有可无]
function sort<T>(data: T, count?: number): T//分工明确
function sort(data: any, count: number = 5): any {
  if (typeof data === "string") {//如果是字符串
    return strSelfSort(data, count)// 按照字符串自排序
  }
  if (data instanceof Array) {//如果data是数组
    if (isChinese(data)) {//如果是中文数组
      return sortChinese(data);
    }
    let newArr = data.map((item) => {
      return typeof item === "string" ? strSelfSort(item) : item
    })
    //英文、数字数组排序
    return quickSort(newArr as any);
  }
}
export { }
  1. 结合 Vue3 源码掌握泛型函数重载
export function ref<T extends object>(value: T): ToRef<T>
export function ref<T>(value: T): Ref<UnwrapRef<T>>
export function ref<T = any>(): Ref<T | undefined>
export function ref(value?: unknown) {
  return createRef(value)
}

泛型工厂函数

  1. 工厂函数类型定义:代表任意一个类的构造函数【等价JS的构造函数】的函数类型
  2. 泛型工厂函数定义:一个可以创建任意类对象的通用泛型函数
  3. 泛型工厂函数应用场景:
    • 使用场景1:在一些不方便或者没有办法直接 new 类名()格式来创建对象,例如:装饰器中就多次用到。
    • 使用场景2:在一些项目测试或者调试中简化代码使用。
type promiseFuncType = (...args: any[]) => any
class ShopCartService {
  public productname: string;
  public count: number;
  constructor() {

  }
  addShopCart() {
    console.log("增加商品:", this.productname + ":数量:", this.count)
  }
}
class Promise {
  constructor() {
    return this
  }
  static Promise(promiseFunc2: promiseFuncType): void {
    promiseFunc2("sucess", "fail");
    //return this
  }
  buy() {
    console.log("buy")
  }
  static ShowResult(promiseFunc3: promiseFuncType, errorCode: string) { }
}
let shopCartService = new ShopCartService()
shopCartService.productname = "牙膏"
shopCartService.count = 70
// 泛型工厂函数类型constructorFuncType
type constructorFuncType<T = any> = new (...args: any[]) => T

// 构建泛型工厂函数写法1:参数类型为type定义的构造函数类型
function createInstanceFactory<T>(promiseFunc2: constructorFuncType<T>) {
  return new promiseFunc2()
}
let promise = createInstanceFactory<Promise>(Promise);
promise.buy()

let shopCartService2 = createInstanceFactory<ShopCartService>(ShopCartService);
shopCartService2.addShopCart();

// 构建泛型工厂函数写法2:参数类型为接口式的构造函数类型
interface constructorFuncinterface<T = any> {
  new(...args: any[]): T
}
//function createInstanceFactory2<T>
//(promiseFunc2: { new(...args: any[]): T }): T {
function createInstanceFactory2<T>
  (promiseFunc2: constructorFuncinterface<T>): T {
  return new promiseFunc2()
}

let promise2 = createInstanceFactory<Promise>(Promise);
promise2.buy()

// 构建泛型工厂函数写法3:参数类型为直接写出来的构造函数类型
function createInstanceFactory3<T>
  (promiseFunc2: new (...args: any[]) => T): T {
  return new promiseFunc2()
}
let promise3 = createInstanceFactory<Promise>(Promise);
promise3.buy()

export { }

TS 交叉类型

  1. 和联合类型区别

    • 赋值区别对于对象类型合成的交叉类型是多个类型属性和方法的合并后的类型,属于多个类型的并集,必须是两个类型的全部属性和方法才能赋值给交叉类型变量。【可选属性和方法除外】。对于对象类型合成的联合类型变量可以接受联合类型中任意一种数据类型全部属性和方法,也可以是两个类型的全部属性和全部方法【可选属性和方法除外】,也可以是一种数据类型全部属性和方法+其他类型的某个属性和某个方法。
    • 获取属性和方法区别: 交叉类型变量可以获取两个类型的任意属性和任意方法,而联合类型的变量只能获取两个类型的共同属性和方法【交集属性和交集方法】
  2. 交叉类型应用场景

    • 可应用这些没有关联的对象合并上,因为这样会极大的方便前端页面的输出。合并如同打包,比单一的一个一个的筛选输出要方便很多,整体感要好很多。
    • 一些 UI 库底层如果用到多个密切连接在一起的关联类型时,可以使用交叉类型来合并输出。
// 如何合并输出下面3个接口类型的对象?使用交叉类型最合适。
interface Button {
  type: string
  text: string
}

interface Link {
  alt: string
  href: string
}

interface Href {
  linktype: string
  target: Openlocation
}

enum Openlocation {
  self = 0,
  _blank,
  parent
}
  1. 泛型函数+交叉类型+类型断言+枚举应用
function cross<T extends object, U extends object>(objOne: T, objTwo: U): T & U {
  let obj = {}
  let combine = obj as T & U
  Object.keys(objOne).forEach((key) => {
    combine[key] = objOne[key]
  })
  Object.keys(objTwo).forEach((key) => {
    if (!combine.hasOwnProperty(key)) {
      combine[key] = objTwo[key]
    }
  })
  return combine;
}
  1. 泛型函数重载+交叉类型+类型断言应用
function cross<T extends object, U extends object>(objOne: T, objTwo: U): T & U
function cross<T extends object, U extends object, V extends object>
  (objOne: T, objTwo: U, objThree: V): T & U & V
function cross<T extends object, U extends object, V extends object>
  (objOne: T, objTwo: U, objThree?: V) {
  let obj = {}
  let combine = obj as T & U

  Object.keys(objOne).forEach((key) => {
    combine[key] = objOne[key]
  })
  Object.keys(objTwo).forEach((key) => {
    if (!combine.hasOwnProperty(key)) {
      combine[key] = objTwo[key]
    }
  })
  if (objThree) {//如果有第三个对象传递进来实现交叉
    //let obj = {}
    //let combine2 = obj as T & U & V
    //let combine2=combine as T & U & V // T & U 是 T & U & V 的子集,所以可以断言
    let combine2 = combine as typeof combine & V
    Object.keys(objThree).forEach((key) => {
      if (!combine2.hasOwnProperty(key)) {
        combine2[key] = objThree[key]
      }
    })
    return combine2// 三个对象交叉结果
  }
  return combine;// 两个对象交叉结果
}