likes
comments
collection
share

详解 Typescript 泛型函数

作者站长头像
站长
· 阅读数 4

一. 泛型函数

泛型函数格式1:

函数名<泛型类型〉(参数中可以使用泛型类型):返回值也可以是泛型类型
泛型函数格式2:函数名<泛型类型1,泛型类型2>(参数中可以使用泛型类型):返回值也可以是泛型类型

案例

1. 快速排序

const quickSort = function <T>(arr: Array<T>): Array<T> {
  if (arr.length < 1) {
    return arr
  }
  let midIndex = Math.floor(arr.length / 2)
  let mid = arr.splice(midIndex, 1)[0]
  let leftArr: Array<T> = [], rightArr: Array<T> = [];

  for (let i = 0; i < arr.length; i++) {
    if (arr[i] < mid) leftArr.push(arr[i]); else rightArr.push(arr[i])
  }
  return [...quickSort(leftArr), mid, ...quickSort(rightArr)]
}

// function quickSort<T>(arr: Array<T>)

2. 泛型函数重载

1. 🀄中文排序

// 1
const sortChinese = function <T extends string>(arr: Array<T>): Array<T> {
  return arr.sort((firstStr, secondStr) => {
    return firstStr.localeCompare(secondStr, 'zh-CN')
  })
}

// 2
const sortChinese = function <T>(arr: Array<T>): Array<T> {
  return arr.sort((firstStr, secondStr) => {
    // 解决T上方法不存在问题
    return (firstStr as any).localeCompare(secondStr, 'zh-CN')
  })
}

2. 字符串排序

let str = 'bacf'

const sortString = function (str: string) {
  let stringArray = str.split('')
  // 数组使用快速排序进行排序
  let sortArray = quickSort(stringArray)
  return sortArray.join('')
}

3. 数组混序

// 判断是否中文
const isChinese = function <T>(arr: Array<T>): boolean {
  let pattern = /[\u4e00-\u9fa5]+/g
  return arr.some(item => {
    return pattern.test(item as string)
  })
}

// 中文+英文 混合排序
const sort = function <T>(arr: Array<T>): Array<T> {
  // 中文数组
  if (isChinese(arr)) {
    return sortChinese(arr)
  }

  let newArr = arr.map(item => {
    return typeof item === 'string' ? sortString(item) : item
  })
	// 英文,数字数组
  return quickSort(newArr as any)
}

4. 中文,英文,数字联合排序(瑕疵版)

const quickSort = function <T>(arr: Array<T>): Array<T> {
  if (arr.length < 1) {
    return arr
  }
  let midIndex = Math.floor(arr.length / 2)
  let mid = arr.splice(midIndex, 1)[0]
  let leftArr: Array<T> = [], rightArr: Array<T> = [];

  for (let i = 0; i < arr.length; i++) {
    if (arr[i] < mid) leftArr.push(arr[i]); else rightArr.push(arr[i])
  }
  return [...quickSort(leftArr), mid, ...quickSort(rightArr)]
}

const sortChinese = function <T>(arr: Array<T>): Array<T> {
  return arr.sort((firstStr, secondStr) => {
    return (firstStr as any).localeCompare(secondStr, 'zh-CN')
  })
}

const sortString = function (str: string, count: number = 5) {
  let stringArray = str.split('')
  let sortArray = quickSort(stringArray)
  let resStr = sortArray.join('')
  return resStr.length > 10 ? resStr.slice(0, count) : resStr
}

// 判断是否中文
const isChinese = function <T>(arr: Array<T>): boolean {
  let pattern = /[\u4e00-\u9fa5]+/g
  return arr.some(item => {
    return pattern.test(item as unknown as string)
  })
}

const isString = function (val: any): val is string {
  return typeof val === 'string'
}

// 中文+英文 混合排序
const sort = function <T>(data: T, count: number = 5): Array<T> | string | undefined {
  if (isString(data)) {
    return sortString(data, count)
  }
  if (data instanceof Array) {
    if (isChinese(data)) {
      return sortChinese(data)
    }

    let newArr = data.map(item => {
      return typeof item === 'string' ? sortString(item) : item
    })

    return quickSort(newArr as any)
  }
}

// console.log(sort('bascbzs'));
console.log(sort(['这里', '哈哈']));

详解 Typescript 泛型函数断言错误!!

5. 泛型函数重载方式

const quickSort = function <T>(arr: Array<T>): Array<T> {
  if (arr.length < 1) {
    return arr
  }
  let midIndex = Math.floor(arr.length / 2)
  let mid = arr.splice(midIndex, 1)[0]
  let leftArr: Array<T> = [], rightArr: Array<T> = [];

  for (let i = 0; i < arr.length; i++) {
    if (arr[i] < mid) leftArr.push(arr[i]); else rightArr.push(arr[i])
  }
  return [...quickSort(leftArr), mid, ...quickSort(rightArr)]
}

const sortChinese = function <T>(arr: Array<T>): Array<T> {
  return arr.sort((firstStr, secondStr) => {
    return (firstStr as any).localeCompare(secondStr, 'zh-CN')
  })
}

const sortString = function (str: string, count: number = 5) {
  let stringArray = str.split('')
  let sortArray = quickSort(stringArray)
  let resStr = sortArray.join('')
  return resStr.length > 10 ? resStr.slice(0, count) : resStr
}

// 判断是否中文
const isChinese = function <T extends string>(arr: Array<T>): boolean {
  let pattern = /[\u4e00-\u9fa5]+/g
  return arr.some(item => {
    return pattern.test(item)
  })
}

const isString = function (val: any): val is string {
  return typeof val === 'string'
}

// 中文+英文 混合排序
// 字符串重载(可省略)
function sort(data: string): string
// 数组重载
function sort<T>(data: T, count?: number): T
// 实现签名
function sort(data: any, count: number = 5): any {
  if (isString(data)) {
    return sortString(data, count)
  }
  if (data instanceof Array) {
    if (isChinese(data)) {
      return sortChinese(data)
    }

    let newArr = data.map(item => {
      return typeof item === 'string' ? sortString(item) : item
    })

    return quickSort(newArr as any)
  }
}

console.log(sort('bascbzs'));
// console.log(sort(['这里', '哈哈']));

ref函数重载签名

// 函数重载
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)
}
export declare interface Ref<T = any>{
  value: T;
  [RefSymboll]: true
}

二. 泛型工厂函数

工厂函数类型定义:代表所有类的构造函数【等价JS的构造函数】的函数类型

泛型工厂函数定义:一个可以创建任意类对象的通用函数 工厂函数应用场景:

使用场景1:在一些不方便直接 new 类名()格式来创建对象的创建,例如:后面讲解的装饰器中就多次用到。

使用场景2:在一些项目测试或者调试中简化代码使用。

简略promise

type PromiseFuncType = (resolve: string, reject: string) => any

class MYPromise {
  constructor(promiseFunc: PromiseFuncType) {
    promiseFunc('success', 'fail')
  }
}

let p = new MYPromise(function(resolve, reject): void {
  console.log(resolve, reject);
})

1. interface的两种方式

// 1
type commonFunc = (...args: any) => any
// 当作函数类型使用
interface commonFuncInter {
  (...args: any): any
}

// 2
// 当作对象类型使用
interface commonFuncInter {
  eat: (...args: any) => any
}
let func: commonFuncInter[eat] = function(count: string, money: number): void {}

2. 类的类型

class CommercialBank {
  public address: string = 'bj'
  public name: string = 'ww'
  static count: number

  constructor(name: string, address: string) {
    this.address = address
    this.name = name
  }
  loan(): void {
    console.log(this.name + "银行贷款");

  }
}

// 类的抽象
type CommercialBankType = new (...arg: any) => CommercialBank
type ConstructorType = new (...arg: any) => any

let Constructor: ConstructorType = CommercialBank
let p1 = new Constructor('广大银行', '哈哈园')

工厂函数类型定义:代表任意一个类的构造函数【等价Js的构造函数】的函数类型。

1.类构造函数对象变量

2.创建类对象的一个类型

3. 工厂函数

// 相当于约束constructor
function createInstanceFactory (Constructor: { new (...arg: any):any }) {
  console.log(Constructor.name +"被创建对象")
  new Constructor("广大银行""万绿园")
}

4. 泛型工厂函数


function createInstanceFactory<T>(Constructor: { new (...arg: any): T }) {
  console.log(Constructor.name +"被创建对象")
  new Constructor("广大银行""万绿园")
}

// 类型 -> CommercialBank 和 类 -> CommercialBank
let p3 = createInstanceFactory<CommercialBank>(CommercialBank)

5. 返回值类型约束

type MyClassDecorator = <T>(targetClass: new (...args: any[]) => T) => void

function Controller(rootPath: string): MyClassDecorator {
  return function(targetClass) {}
}

// 或者
type MyClassDecorator = <T>(targetClass: { new (...args: any[]): T }) => void

三. 泛型函数&交叉类型

交叉类型是多个类型属性和方法的合并后的类型,属于多个类型的并集,必须是两个类型的全部属性和方法才能赋值给交叉类型变量。【可选属性和方法除外】

而联合类型的变量可以接受联合类型中任意一种数据类型全部属性和方法,也可以是两个类型的全部属性和全部方法【可选属性和方法除外】

获取属性和方法区别: 交叉类型变量可以获取两个类型的任意厲性和任意方法,而联合类型的变量只能获取两个类型的共同属性和方法【交集属性和交集方法】

交叉类型应用场景: 通常用于多个对象合并的场景。比如:我们把用户信息,用户角色信息合并成一个对象然后输出。当然后端可以通过连接查询的SQL 语句来完成到前端的多对象输出,但大多需要表的外键来支持,比如用户和角色就需要 角色表有用户外键,对于现实生活中有必须关联在一起的实体【比如商品和库存信息】一般建议数据表用外键来支持前端多个对象的合并输出,虽然影响了效率,但也保证了表的数据合理性和完整性。

但如果我们临时需要随机把两个根本没有外键关联的数据表取出来的对象合并在一起输出,比如用户信息和日志信息,商品信息和日志信息,订单信息和日志信息,我们就可以用交叉类型来完成。因为我们不可能为了这个临时的对象合并需求把所有的这些表都建立起外键,须知外键太多不仅增加了数据表维护的负担,而且也有较大的影响了表操作效率。

交叉类型合并两个对象

type objType1 = { username: string, age: number }
type objType2 = { custname: string, phone: number }


// 合并两个对象
let obj = {} as objType1 & objType2
let first: objType1 = { username: '小明', age: 17 }
let second: objType2 = { custname: '小花', phone: 123456 }

Object.keys(first).forEach(key => {
  obj[key] = first[key]
})
Object.keys(second).forEach(key => {
  obj[key] = second[key]
})

console.log(obj);

泛型实现合并对象

let button = {
  btntype: 'normal',
  text: "跳转到百度"
}

let link = {
  alt: 'goto baidu',
  href: 'http://www.baidu.com'
}

let href = {
  inktype: "外网",
  target: 'Openlocation._blank'
}

function cross<T extends object, U extends object>(obj1: T, obj2: U): T & U {
  // 推断
  let combine = {} as T & U
  Object.keys(obj1).forEach(key => {
    combine[key] = obj1[key]
  })
  Object.keys(obj2).forEach(key => {
    // 是否已经存在?
    if (!combine.hasOwnProperty(key)) {
      combine[key] = obj2[key]
    }
  })
  return combine
}

let c1 = cross(button, link)
console.log(c1);

{
  btntype: 'normal',
  text: '跳转到百度',
  alt: 'goto baidu',
  href: 'http://www.baidu.com'
}

泛型重载实现多个交叉类型合并

let button = {
  btntype: 'normal',
  text: "跳转到百度"
}

let link = {
  alt: 'goto baidu',
  href: 'http://www.baidu.com'
}

let href = {
  inktype: "外网",
  target: 'Openlocation._blank'
}

function cross<T extends object, U extends object>(obj1: T, obj2: U): T & U
function cross<T extends object, U extends object, V extends object>(obj1: T, obj2: U, obj3: V): T & U & V
function cross<T extends object, U extends object, V extends object>(obj1: T, obj2: U, obj3?: V) {
  let combine = {} as T & U
  Object.keys(obj1).forEach(key => {
    combine[key] = obj1[key]
  })
  Object.keys(obj2).forEach(key => {
    if (!combine.hasOwnProperty(key)) {
      combine[key] = obj2[key]
    }
  })

  if (obj3) {
    // T & U 为 T & U & V 的子集,所以可以进行代码运行前的推断
    // let combineUnion = combine as T & U & V
    let combineUnion = combine as typeof combine & V
    Object.keys(obj3).forEach(key => {
      if (!combine.hasOwnProperty(key)) {
        combineUnion[key] = obj3[key]
      }
    })
    return combineUnion
  }

  return combine
}

let c1 = cross(button, link, href)
console.log(c1);
{
  btntype: 'normal',
  text: '跳转到百度',
  alt: 'goto baidu',
  href: 'http://www.baidu.com',
  inktype: '外网',
  target: 'Openlocation._blank'
}