TS 泛型

273 阅读3分钟

泛型作用

使用泛型可以创建可重用的组件,一个组件可以支持多种类型的数据

为啥会出现泛型

让某个函数适用于任何特定的类型

简单例子

function test<T>(value:T):T {
    return value 
}
console.log(test<Number>(1))  // 1

// 总结
<T>内部的T被称为类型变量,希望传递给test函数的类型占位符,同时它被分配给value参数用来代替它的类型:此时T充当的是类型
  • 额外扩展
    • T(Type):在定义泛型时通常用作第一个类型变量名称
    • K(Key):表示对象中的键类型
    • V(Value):表示对象中的值类型
    • E(Element):表示元素类型 并不是只能定义一个类型变量,可以引入希望定义的任何数量的类型变量。

面试题:如何返回两种类型的对象?

  • 使用元祖,即为元祖设置通用的类型:
function test<T,U>(value:T,message:U):<T,U> {
   return [value,message]
}
  • 函数重载 定义:函数重载或方法重载是使用相同名称和不同参数数量或类型创建多个方法的一种能力。
type Types = number | string
function add(a:number,b:number):number
function add(a:string,b:string):string
function add(a:string,b:number):string
function add(a:number,b:string):string
function add(a:Types,b:Types) {
   if(typeof a === 'string' || typeof b === 'string'){
     return a.toString() + b.toString()
   }
   return a + b
}
const result = add('nihao','ts')
result.split(' ')
  • 泛型接口 先创建一个用于test函数调用test接口:
interface Test<V,M> {
   value:V,
   message:M
}

将Test接口作为test函数的返回类型

function test<T,U>(value:T,message:U):Test<T,U> {
   console.log(value + ':' + typeof (value))  // 12:number
   console.log(message + ':' + typeof (message))  // nihao:string
   let testData:Test<T,U> = {
      value,
      message
   }
   return testData
}
console.log(test(12,'nihao')) // {value:12,message:'nihao'}

泛型约束

定义:限制每个类型变量接受的类型数量

  • 确保属性存在 背景:有时,希望类型变量对应的类型上存在某些属性,除非显式地将特定属性定义为类型变量,否则编译器不知道它的存在。 例子:处理字符串或数组时,会假设length属性是可用的,可编译器会报错,因为不知道是否含有length属性
function test<T>(arg:T):T{
   console.log(arg.length)//Error
   return arg
}
// 修改后
interface Length {
   length:number
}
function test<T extends Length>(arg:T):T {
   console.log(arg.length)//可以获取length属性
   return arg
}

T extends Length用于告诉编译器,已经实现Length接口的任何类型。 另一种方式:使用,分割多种约束类型,比如:<T extends Length,Type2,Type3>.

function test<T>(arg:T[]):T[] {
   console.log(arg.length)
   return arg
}
// or
function test<T>(arg:Array<T>):Array<T> {
   console.log(arg.length)
   return arg
}
  • 检查对象上的键是否存在 泛型约束另一个使用场景就是检查对象上的键是否存在。 keyof操作符可以用于获取某种类型的所有键,返回类型时联合类型。
interface Person {
   name:string;
   age:number;
   location:string;
}
type K1 = keyof Person; // "name" | "age" | "location"
type k2 = keyof Person[]; // number | "length" | "push"|...
type k3 = keyof { [x:string]:Person};  // string | number 

通过keyof操作符,就可以获取指定类型的所有键。

通过k extends keyof T可以确保参数key一定是对象中含有的键。

enum Colors {
   red,
   blue,
   green
}
function getProperty<T,K extends keyof T>(obj:T,key:K):T[K] {
   return obj[key]
}
let tsInfo = {
   name:'nihao',
   age:121,
   diff:Colors.red
}
let diff:Colors = getProperty(tsInfo,'diff')  // ok

泛型工具类型

  • Partial:将某个类型里的属性全部变为可选项 ?
type Partial<T> = {
   [p in keyof T] ?  : T[p]
}

// 通过keyof T 拿到T的所有属性名,然后使用in遍历,将值赋给P,最后通过T[p]取得相应的属性值。中间的?号,用于将所有属性变为可选
  • Record<K extends keyof any,T>:将K中所有的属性的值转化为T类型 定义:
type Record<K extends keyof any,T> = {
   [P in K ]: T
}
  • Pick<T,K extends keyof T>:将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型。 定义:
type Pick<T,K extends keyof T> = {
   [P in K]:T[P]
}
  • Exclude<T,U>将某个类型中属于另一个的类型移除掉 定义:
type Exclude<T,U> = T extends U ? never :T;
//如果T能赋值给U类型的话,那么就会返回never类型,否则返回T类型。
  • ReturnType<T>作用是用于获取函数T的返回类型

定义:

type ReturnType <T extends (...args:any) => any> = T extends(...arg:any) = infer R ? R :any;

使用泛型创建对象