Typescript 实战 --- (6)泛型

1、什么是泛型?
 
不预先确定的数据类型,具体的类型在使用的时候才能确定
function log<T>(value: T) {
  return value;
}

// 调用方式
log<string[]>(['a', 'b'])

// 还可以根据类型推断省略掉类型的参数
log(['a', 'b'])

 

如果使用 any类型来定义这个函数,则意味着该函数传入的参数和返回值都可能是任意类型,例如:传入一个字符串类型,返回的却是数字类型
function log(value: any): any {
  return value;
}

 

2、泛型函数类型
function identity<T>(arg: T): T {
  return arg;
}

let myIdentity: <T>(arg: T) => T = identity;

 

3、泛型接口
interface Log1 {
  <T>(value: T): T   // 使用泛型约束一个函数
}

interface Log2<T> {  // 使用泛型约束接口的所有成员
  (value: T): T
}

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

// 实现泛型接口的时候,必须指定类型
let myLog: Log2<number> = log;
myLog(2);

// 如果不指定类型,也可以在泛型接口中指定一个默认的类型
interface Log3<T = string> {
  (value: T): T
}

let myLog3: Log3 = log;
myLog3('hello');

 

4、泛型类
 
泛型不能用来约束类的静态成员,只能约束类的实例成员
class Log<T> {
  run(value: T): T {
    return value
  }

  // static foo(value: T): T { return value }    // 静态成员不能引用类类型参数
}

let log1 = new Log<number>();
log1.run(2);

let log2 = new Log();
log2.run('hello');

 

5、泛型约束
 
上例中的 log函数,如果我们想要访问其函数参数的length属性,程序会报错,因为不是所有类型都有length属性
function log<T>(value: T): T {
  console.log(value.length);   // 类型“T”上不存在属性“length”
  return value;
}
 
为此,我们要列出对 T 的约束要求,传入的类型必须有这个属性才行。可以通过定义一个接口来描述约束条件,使用这个接口和extends关键字来实现约束
interface HasLength {
  length: number;
}

function log<T extends HasLength>(value: T): T {
  console.log(value.length);   
  return value;
}

log([1]);   // 1
log('hello');   // 5
log({length: 2});   // 2
log(2);   // 类型“2”的参数不能赋给类型“HasLength”的参数

 

6、使用泛型的好处

(1)、增强程序的可扩展性:函数或类可以很轻松的支持多种数据类型

(2)、增强代码的可读性:不必写多条函数重载,或者冗长的联合类型声明

(3)、灵活的控制类型之间的约束
posted @ 2020-01-16 15:40  rogerwu  阅读(576)  评论(0编辑  收藏  举报