重学TS --- generic

320 阅读3分钟

这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战

// 泛型基本定义
// 此时T就是类型变量,而这个类型变量一般被称之为泛型
// 泛型一般是一个大写的字母,不一定是T,T只是形参名
// 在函数的定义中,泛型一般写在喊名后,参数列表前
const getArr = <T>(item: T, length: number) => {
  return new Array(length).fill(item)
}

// 在函数名后,括号前加上<>,在其中写上实际需要使用的泛型值
// 也就是类型变量的实参值
console.log(getArr<number>(3, 3)) // => [3, 3, 3]

// 类型不一致 error
// console.log(getArr<number>('3', 3)) // error

// 如果不指定具体的泛型值
// 那么TS会根据传入的参数自动去推断泛型所对应的具体值
console.log(getArr(3, 3)) // => [3, 3, 3]
// 在一个函数中,可以使用多个泛型
// 对于这个函数需要手动指定函数的返回值类型,因为TS无法准确推断出函数的返回值类型
function getTupleArr<T, K>(param1: T, param2: K, length: number): Array<[T, K]> {
  return new Array(length).fill([param1, param2])
}

// 此时T和K的值都是string
console.log(getTupleArr('key', 'value', 5))

泛型在不同类型中的使用

定义函数

// 定义函数接口
let sumFun: <T, V>(param1: T, param2: V) => number

// 虽然在这里使用的使用parm1和param2的类型是any
// 但是因为sumFun函数定义中有泛型约束
// 所以参数param1和param2的类型有具体的类型
sumFun = (param1: any, param2: any) => param1 + param2

console.log(sumFun(10, 20))

类型别名

// 定义类型别名
type sumType = <T, V>(param1: T, param2: V) => number

const sumFun: sumType = (param1: any, param2: any) => param1 + param2

console.log(sumFun(10, 20))

定义接口

// 定义类型别名
// 如果一个接口中只有函数的定义,那么推荐使用类型别名来定义该函数的数据类型
interface Isum {
  <T, V>(param1: T, param2: V): number
}

const sumFun: Isum = (param1: any, param2: any) => param1 + param2

console.log(sumFun(10, 20))
// 泛型也可以定义在函数接口上
// 这样接口中的所有数据都可以使用对应的泛型变量
interface ISum<T, V> {
  (param1: T, param2: V): number
  arr: Array<[T, V]>
}

泛型约束

虽然我们可以使用泛型来定义类型变量,但是我们发现对于传递给泛型变量的类型其实是没有任何的约束的,如果我们需要限制可以赋值给泛型变量的值的类型的时候就可以使用泛型约束

// 泛型变量T继承自接口{ length: number }
// 就表明可以赋值给泛型变量T的数据类型必须是函数length属性的对象
function getLength<T extends { length: number }>(param: T) {
  console.log(param.length)
}

getLength('abc') // => 3
// getLength(3) // error
// keyof O ---> O的所有key组成的联合类型
function getProperty<O, K extends keyof O>(obj: O, key: K) {
  return obj[key]
}

const obj = {
  name: 'Klaus',
  age: 24
}

console.log(getProperty(obj, 'name'))

定义类类型

在TS中,一个类所创建出来的实例的类型就是该类

此时这个类即使数据也是类型

// foo: new(username: string) => T
// 表示foo是一个类,该类的构造函数在使用的时候需要传递一个类型为string的变量
// getInstance方法的返回值是foo这个类的实例对象
function getInstance<T>(foo: new(username: string) => T, username: string) {
  return new foo(username)
}

class Foo {
  constructor(public name: string) {
    this.name = name
  }
}

// 所创建的实例的类型为 Foo
console.log(getInstance(Foo, 'Klaus'))