目录
前言
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中的泛型,下次再见!