一、概念

不预先确定的数据类型,具体的类型在使用的时候才能确定。
泛型约定,函数的输入类型和输出类型必须一样。

  1. function log<T>(value: T): T {
  2. console.log(value)
  3. return value;
  4. }
  5. // 调用该函数的时候,传入的是什么类型,输出的也是什么类型
  6. console.log(log([1, 2])) // [1, 2]
  7. console.log(log(1,2)) // 1, 2
  8. log<string[]>(['a', 'b'])

二、泛型函数

function log<T>(value: T): T {
    return value;
}

console.log(log([1, 2]))

// 泛型函数
type Log = <T>(value: T) => T;
let myLog: Log = log;

三、泛型接口

// 泛型接口
// 第一种写法:
// interface Log {
//     <T>(value: T): T;
// }

// 第二种写法
// 这样接口的所有成员都受到泛型约束
// 实现的时候,必须只能类型
// interface Log<T> {
//     (value: T): T;
// }
// let myLog: Log<number> = log
// myLog(1)
// 这时myLog的参数只能是number

// 第三种写法,在接口泛型上添加默认类型
interface Log<T = string> {
    (value: T): T;
}
let myLog: Log = log
myLog('aaa')

四、泛型类

// 泛型也可以约束类的成员。
// 泛型不能应用类的静态成员

// 泛型类
class Log<T> {
    run (value: T) {
        return value
    }
}
let log1 = new Log<number>()
log1.run(1)
let log2 = new Log()
log2.run('1')

五、泛型约束

// 泛型约束
interface Length {
    length: number
}
function log<T extends Length>(value: T) {
    return value
}
// 这样不管参数是什么,都要有length属性
// log(1) // 不行
log([1]) // 可以

六、泛型的好处

  • 函数和类可以轻松的支持多种类型,增强程序的扩展性
  • 不必写多条函数重载,冗长的联合类型声明,增强代码的可读性
  • 灵活控制类型之间的约束