在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型。

函数泛型

需求:定义一个函数,接收两个参数,第一个是数据,第二个是数量,生成一个数据*数量的数组

  1. (() => {
  2. // 根据传入的数据和数量,生成一个数据*数量的数组
  3. function getArr(value:any,count:number):any[]{
  4. const arr:any[]=[]
  5. for (let i=0;i<count;i++){
  6. arr.push(value)
  7. }
  8. return arr
  9. }
  10. console.log(getArr(1,3))
  11. console.log(getArr('1',3))
  12. })()
  13. // 输出结果
  14. // (3) [1, 1, 1]
  15. // (3) ["1", "1", "1"]

多个泛型参数的函数

一个函数中可以有多个泛型参数

  1. (() => {
  2. // 使用k和v表示不同的值类型,所有引用了K的变量类型一致,V也是
  3. function swap<K, V>(a: K, b: V): [K, V] {
  4. return [a, b]
  5. }
  6. // 调用的时候指定参数的类型
  7. const result = swap<string, number>('abc', 123)
  8. // ts编译器识别出变量的类型,并提供相关的方法提示
  9. console.log(result[0].length, result[1].toFixed())
  10. })()
  11. // 输出结果
  12. // 3 "123"

泛型接口

在定义接口时, 为接口中的属性或方法定义泛型类型 在使用接口时, 再指定具体的泛型类型

  1. (() => {
  2. // 在定义接口时, 为接口中的属性或方法定义泛型类型
  3. interface IbaseCRUD <T> {
  4. data: T[]
  5. add: (t: T) => void
  6. }
  7. // 在使用接口时, 再指定具体的泛型类型
  8. // 定义一个类,并指定成员属性的值类型
  9. class User {
  10. id?: number; //id主键自增
  11. name: string; //姓名
  12. age: number; //年龄
  13. // 构造方法,更新属性数据
  14. constructor (name, age) {
  15. this.name = name
  16. this.age = age
  17. }
  18. }
  19. // 定义一个类,这个类的类型就是上面定义的接口,传入上面定义的类
  20. class UserCRUD implements IbaseCRUD <User> {
  21. data: User[] = []
  22. add(user: User): void {
  23. user = {...user, id: Date.now()}
  24. this.data.push(user)
  25. console.log('保存user', user.id)
  26. }
  27. }
  28. // 实例化对象
  29. const userCRUD = new UserCRUD()
  30. // 调用add方法添加用户到user列表
  31. userCRUD.add(new User('tom', 12))
  32. userCRUD.add(new User('tom2', 13))
  33. console.log(userCRUD.data)
  34. })()
  35. // 输出结果
  36. // 保存user 1614172477494
  37. // 保存user 1614172477495
  38. //
  39. // (2) [{…}, {…}]
  40. // 0: {name: "tom", age: 12, id: 1614172477494}
  41. // 1: {name: "tom2", age: 13, id: 1614172477495}
  42. // length: 2

泛型类

在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型

  1. (() => {
  2. // 定义一个类, 类中的属性和方法定义泛型类型(T表示同种类型)
  3. class GenericNumber<T> {
  4. zeroValue: T
  5. add: (x: T, y: T) => T
  6. }
  7. // 实例化对象,指定类型为number
  8. let myGenericNumber = new GenericNumber<number>()
  9. myGenericNumber.zeroValue = 0
  10. myGenericNumber.add = function (x, y) {
  11. return x + y
  12. }
  13. // 调用对象的方法,传入number参数
  14. console.log(myGenericNumber.add(myGenericNumber.zeroValue, 12))
  15. // 实例化对象,指定类型为string
  16. let myGenericString = new GenericNumber<string>()
  17. myGenericString.zeroValue = 'abc'
  18. myGenericString.add = function (x, y) {
  19. return x + y
  20. }
  21. // 调用对象的方法,传传入string参数
  22. console.log(myGenericString.add(myGenericString.zeroValue, '-def'))
  23. })()
  24. // 输出结果
  25. // 12
  26. // abc-def

泛型约束

没有泛型约束

  1. (() => {
  2. function fn <T>(x: T): void {
  3. // 对一个泛型参数取 length 属性, 会报错, 因为这个泛型根本就不知道它有这个属性
  4. console.log(x.length)
  5. }
  6. })()
  7. // 输出结果
  8. error TS2339: Property 'length' does not exist on type 'T'.

使用泛型约束

  1. (() => {
  2. // 定义一个泛型约束
  3. interface Lengthwise {
  4. length: number;
  5. }
  6. // 指定泛型约束
  7. function fn2<T extends Lengthwise>(x: T): void {
  8. console.log(x.length)
  9. }
  10. fn2('abc')
  11. // 当传入值不符合约束类型时,会报错:
  12. fn2(123) // error number没有length属性
  13. })()
  14. // 输出结果
  15. // 3
  16. // error TS2345: Argument of type 'number' is not assignable to parameter of type 'Lengthwise'.