:::warning 泛型是指在定义函数、接口或类的时候,
不预先指定具体的类型,而在使用的时候再指定类型的一种特性 :::

在我们声明一个函数的时候,并且这个函数的入参可以是任意类型的时候,我们很难去做到确定返回的参数类型。

  1. function echo(arg:any):any{
  2. return arg
  3. }
  4. // const result: any
  5. // 这里的result返回的类型还是any.
  6. const result = echo(123)

如何使用泛型来确定类型

  1. // 我们可以使用<>符号,里面定义一个占位符,这个占位符可以是任意字段
  2. function echo<T>(arg:T):T{
  3. return arg
  4. }
  5. // 这个时候他的类型不再是any了
  6. // const result: 123
  7. const result = echo(123)
  8. // const result: true
  9. const result = echo(true)
  1. function swap<T, U>(tuple: [T,U]): [U, T]{
  2. return [tuple[1], tuple[0]]
  3. }
  4. // const result: [number, string]
  5. const result = swap(['string',123])

总结

:::warning 其实泛型就相当于一个占位符或者说是一个变量,
在使用的时候,我们可以把定义好的类型像参数一样传入,
然后它原封不动的给我们输出回来 :::

泛型约束 extends

在函数内部使用泛型变量的时候,由于事先不清楚它是哪种类型,所以不能随意的操作它的属性或方法。

:::warning 所谓的泛型约束,其实就是规定传入的类型必须按照我们规定的写法来传入 :::

例如

  1. // 因为泛型T,不一定包含属性length
  2. function echoWithArr<T>(arg: T): T{
  3. console.log(arg.length) // 会报错 类型“T”上不存在属性“length”
  4. return arg
  5. }
  6. // 改造, 但是这种写法只能传数组
  7. function echoWithArr<T>(arg: T[]): T[]{
  8. console.log(arg.length) // 这样就没问题了
  9. return arg
  10. }
  11. const arrs = echoWithArr([1,2,3])
  12. // 所以我们可以对泛型进行一个约束,让他只能传入有length的参数
  13. interface IWithLength {
  14. length: number
  15. }
  16. // 我们可以< 变量 extends 类型 >
  17. function echoWithLength<T extends IWithLength>(arg: T): T{
  18. console.log(arg.length)
  19. return arg
  20. }
  21. // 只要有length属性,都可以,所以这也是鸭子类型的概念
  22. const str = echoWithLength('str')
  23. const obj = echoWithLength({ length:10, width:10})
  24. const arr2 = echoWithLength([1,2,3])
  25. // 报错,类型“number”的参数不能赋给类型“IWithLength”的参数
  26. const num = echoWithLength(13)

泛型与类和接口

当我们在类里面定义一些方法,并且这些方法只有某些类型才有。那么我们在使用该类的方法是有可能会报错,并且ts在编译过程中无法获取错误

:::warning 像下面例子,我们分别添加了数值和字符串,但是我们都调用了toFixed方法,因为toFixed是属于number的方法。
所以str调用一定会报错,但是在下面的例子中,ts无法检测报错,只有在运行时才能发现。 :::

例如

  1. class Queue{
  2. private data = [];
  3. push(item){
  4. return this.data.push(item)
  5. }
  6. pop(){
  7. return this.data.shift()
  8. }
  9. }
  10. const queue = new Queue()
  11. queue.push(1);
  12. queue.push('str')
  13. console.log(queue.pop().toFixed())
  14. console.log(queue.pop().toFixed())

改造一下

  1. class Queue<T>{
  2. private data = [];
  3. push(item:T){
  4. return this.data.push(item)
  5. }
  6. pop():T{
  7. return this.data.shift()
  8. }
  9. }
  10. const queue = new Queue<number>()
  11. queue.push(1);
  12. // 类型“string”的参数不能赋给类型“number”的参数。
  13. queue.push('str') // 报错
  14. console.log(queue.pop().toFixed())
  15. console.log(queue.pop().toFixed())

接口和泛型

:::warning 我们的接口interface也可以使用泛型变得更灵活 :::

  1. // 我们这种写法可以自定义传值,更加灵活
  2. interface KeyPair<T, U> {
  3. key: T;
  4. value: U;
  5. }
  6. let kp1: KeyPair<number, string> = { key:1, value:'str'}
  7. let kp2: KeyPair<string, number> = { key:'str', value:1}
  8. // 我们之前定义的数字类型数组
  9. let arr:number[] = [1,2,3]
  10. // 如果使用泛型和ts内置的interface-number
  11. let arrTwo: Array<number> = [1,2,3]