TS学习笔记(五):泛型

泛型是指定一个表示类型的变量,用它来代替某个实际的类型用于编程,而后通过实际调用时传入或推导的类型来对其进行替换,以达到一段使用泛型程序可以实际适应不同类型的目的。为了实现泛型所要达到的目的,我们也可采用指定类型为 any 或者为每一种可能的类型都写一个方法(重载),但这严重违反抽象和复用代码的原则。所以在考虑可重用组件的时候,我们应该使用泛型。

泛型其实是 C# 和 Java 这种强类型语言中的一个特性,TypeScript 把这一特性引进到了弱类型语言 JavaScript 中,对于没接触过强类型语言的前端 coder,理解这个概念可能有点吃力,如果看不太懂,可以先去了解一下 Java 中的泛型,下面进入正题。

初识泛型

定义泛型:我们把要传入函数的参数类型设为一个类型变量 T ,它能够帮助我们捕获用户传入的类型,之后出现 T 的地方,都会被替换成用户传入的类型。

function identity<T>(arg: T): T {
  return arg;
}

function getFirst<T>(arr: T[]): T {
  return arr[0];
}

console.log(identity(10)); // 10
console.log(identity('TS')); // TS
console.log(getFirst([1, 2, 3, 4])); // 1
console.log(getFirst(['a', 'b', 'c'])); // a
复制代码

使用泛型:有两种使用泛型的方式,第一种是传入所有的参数,包括类型参数;第二种是不传类型参数,因为 TypeScript 的编译器会利用类型推论来确定参数的类型,推荐使用第二种。

function identity<T>(arg: T): T {
  return arg;
}

function getFirst<T>(arr: T[]): T {
  return arr[0];
}

// 使用一:传入所有参数
console.log(identity<number>(10)); // 10
console.log(identity<string>('TS')); // TS
console.log(getFirst<number>([1, 2, 3, 4])); // 1
console.log(getFirst<string>(['a', 'b', 'c'])); // a
// 使用二:不传参数类型
console.log(identity(10)); // 10
console.log(identity('TS')); // TS
console.log(getFirst([1, 2, 3, 4])); // 1
console.log(getFirst(['a', 'b', 'c'])); // a
复制代码

泛型类型

一个泛型函数的类型如下:

<泛型变量名称>(参数1: 泛型变量, 参数2: 泛型变量, ...参数n: 泛型变量) => 泛型变量

复制代码

可以以对象字面量的形式来定义泛型函数(这更像是接口),如:

let foo: { <T>(arg: T): void };
foo = function <T>(arg: T): void {
  console.log(arg);
}
foo(13); // 13

复制代码

将上面的例子中的 { <T>(arg: T): void }改为接口,则有:

interface IGeneric {
  <T>(arg: T): void
}
let foo: IGeneric = function <T>(arg: T): void {
  console.log(arg)
}
foo(13); // 13

复制代码

最后,接口中也可以使用泛型,这样子就锁定了代码里可以使用的类型,如:

interface IGeneric<T> {
  (arg: T): void
}

function fn<T>(arg: T): void {
  console.log(arg);
}

let myFn: IGeneric<number> = fn;
myFn(13); //13
复制代码

泛型类

除了泛型接口,我们还可以创建泛型类(但是无法创建泛型枚举、泛型命名空间),泛型类使用 <> 包围泛型类型变量,如:

class Person<T> {
  love: T;
  say: (arg: T) => T;
}

let me = new Person<string>();
me.love = 'TS';
// me.love = 520; // ERROR
me.say = function(love: string){
  return `my love is ${love}`;
}

console.log(me.say('TS')); // my love is TS
复制代码

注意:类有两部分,分为静态部分和实例部分,泛型类的类型指的是实例部分的类型,静态属性不能使用该泛型类型。

泛型约束

泛型可以通过 extends 一个接口来实现泛型约束,写法如:<泛型变量 extends 接口> ,例子:

interface IArray {
  length: number
}

function logIndex<T extends IArray>(arg: T): void {
  for (let i = 0; i < arg.length; ++i) {
    console.log(i)
  }
}

let arr = [1, 2, 3]
// logIndex<number>(arr) // 报错
logIndex<number[]>(arr) // 允许
logIndex(arr) // 自动类型推导,允许

复制代码

可以在泛型里使用类类型,如使用泛型创建工厂函数,需要引用构造函数的类类型,有:

function create<T>(c: { new(): T }): T {
  return new c()
}

复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值