TypeScript详解八:泛型

前言

ts中的泛型就跟any差不多,不过与any不同的是,any会丢失类型而泛型不会。

一、什么是泛型?

  • 定义:宽泛的类型,指的是在定义函数/接口/类型时,不预先指定具体的类型,而是在使用的时候在指定类型限制的一种特性
  • 作用:解决一个类型不确定时的问题,避免类型定义不清晰。如:定义一个不确定类型时,通常我们可以用any或者泛型,但any的话只能知道传入的类型,无法获取返回值的类型,因此建议使用泛型。

二、泛型基本使用

1.函数中使用泛型

// 创建一个长度为length,值为value的数组
function createArray<T>(length: number, value: T): Array<T> {
  let result: T[] = [];
  for (let i = 0; i < length; i++) {
    result[i] = value;
  }
  return result;
}
// 此处调用时传递string类型,此时value传递其他类型就会报错
// 下面这几种写法等效
let result = createArray(3, 'abc')
// let result: string[] = createArray(3, 'abc')
// let result = createArray<string>(3, 'abc')
// let result: string[] = createArray<string>(3, 'abc')
console.log(result); // [ 'abc', 'abc', 'abc' ]

2.接口中使用泛型

// 普通接口使用泛型
namespace a {
  interface Calculate {
  	// 函数接收T泛型,参数T泛型,返回值T泛型
    <T>(a: T, b: T): T
  }
  let sum: Calculate = function<T> (a: T, b: T): T {
    return a;
  }
  // 传入number类型
  console.log(sum<number>(1, 2)); // 1
}
namespace b {
  interface Calculate<T> {
    (a: T, b: T): T
  }
  let sum: Calculate<number> = function(a: number, b: number): number {
    return a + b;
  }
  console.log(sum(1, 2)); // 3
}

namespace c {
  interface Calculate<T> {
    <U>(a: T, b: T): U
  }
  let sum: Calculate<number> = function<U> (a: number, b: number): U {
    return a as any;
  }
  console.log(sum<number>(1, 2)); // 1
}

// 说明 IArguments 是怎么实现的
// 源码里 IArguments 接口实现如下,采用的是迭代器实现
interface IArguments {
  /** Iterator */
  [Symbol.iterator](): IterableIterator<any>;
}
// 手动模拟IArguments, 实现Symbol.iterator接口,就能被for of迭代
let obj = {
  [Symbol.iterator]: () => {
    let i = 0;
    function next() {
      return { value: i++, done: false}
    }
    return { next }
    }
}
for (let key of obj) {
  if(key < 5) console.log(key); // 0 1 2 3 4 5
}

// es6 的迭代器函数实现迭代
function* gen() {
  yield 1;
  yield 2;
}
let it = gen();
let { value, done } = it.next();
console.log(value, done); // 1 false

3.类中使用泛型

// 定义类的时候声明泛型,在类里使用
class MyArray<T> {
  private list: T[] = [];
  add(value: T) {
    this.list.push(value);
  }
  getFirst(): T {
    return this.list[0];
  }
}

let array = new MyArray<number>();
array.add(1);
array.add(2);
array.add(3);
console.log(array.getFirst()); // 1

4.多个泛型

function swap<A, B>(tuple: [A, B]): [B, A] {
    return [tuple[1], tuple[0]]
  }
  let s = swap<number, string>([1, '2']);
  console.log(s); // [ '2', 1 ]

5.默认泛型

// 定义时赋默认类型,如果未传入类型,那么就执行默认类型
function createArray<T=number>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
      result[i] = value;
    }
    return result;
  }
  let result = createArray<string>(4, 'xxx'); // 此处可以不传类型,因为传了参数,会自动推断类型,因此就是默认泛型
  console.log(result); // [ 'xxx', 'xxx', 'xxx', 'xxx' ]

  // 写法一
  interface T1<T=string> { }
  type T2 = T1;
  // 写法二
  interface T3<T> { }
  type T4 = T3<string>;

6.泛型与new,配置类一起使用

function factory<T>(type: { new(): T }): T {
  return new type();
}
class Person { };
// 传入的时Person,返回的还是Person
let f = factory<Person>(Person);
console.log(f); // Person {}

三、泛型约束

1.此处的extends并不指继承,单指一种状态

// 采用泛型约束的形式实现
interface Calculate {
  <T extends (string | number)>(a: T, b: T): void
}
let sum: Calculate = function <T extends (string | number)>(a: T, b: T): void {
  console.log(a, b);
}
sum<(string | number)>(1, 2);

// 1)
 interface LengthWise {
   length: number
 }
 function logger<T extends LengthWise>(val: T) {
   console.log(val.length); // 5   10
 }
 logger<string>('hello');

 // 2)
 let obj = { length: 10 };
 type Obj = typeof obj;
 logger<Obj>(obj);

四、泛型类型别名

type Cart<T> = { list: T[] } | T[];
let c1: Cart<string> = { list: ['1', '2', '3'] };
let c2: Cart<number> = [1, 2, 3];

// 泛型接口 vs 泛型类型别名
// 接口创建了一个新的名字,它可以在任何地方被调用。而类型别名并不创建新的名字。例如报错信息就不会使用别名
// 类型别名不能被 extends 和 implements ,这时候我们应该尽量使用接口代替类型别名
// 当我们需要使用联合类型或者元组类型的时候,泛型别名会更合适
// 注意:能用 interface 实现的不要用type

总结

该谝文章主要记录ts中的泛型,下次再见!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

剑九_六千里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值