主要阐述了:
- 泛型与使用场景
- 在函数、类、接口中如何使用泛型
为什么要使用泛型
许多时候,标注的具体类型并不能确定,比如一个函数的参数类型
function sort(items: Array<string>, order: 'desc'|'asc'): Array<string> {
//...
}
上面的 sort 函数虽然标注了类型,但是同时也限制了传入的只能是字符串数组。实际上,我们是希望它能传入字符串数组、数字数组、甚至用户自定义的结构类型,这个时候我们希望传入的类型能在具体调用的时候再确定,就像是函数内部可变数据转为参数一样。泛型 - 就可以完成这个需求
泛型的使用
function sort<T>(items: T, order: 'desc'|'asc'): T {
//...
}
所谓的泛型,就是给可变(不定)的类型定义变量(参数),<> 类似 ()
泛型接口
我们还可以在接口中使用泛型
场景 后端提供了一些接口,用以返回一些数据,依据返回的数据格式定义如下接口:
interface IResponseData {
code: number;
message?: string;
data: any;
}
我们会发现该接口的 data 项的具体格式不确定,不同的接口会返回的数据是不一样的
// 用户接口
interface IResponseUserData {
id: number;
username: string;
email: string;
}
// 文章接口
interface IResponseArticleData {
id: number;
title: string;
author: IResponseUserData;
}
这个时候我们可以对 IResponseData 使用泛型
interface IResponseData<T> {
code: number;
message?: string;
data: T;
}
下面是具体代码
function getUser<U>(url: string) {
return fetch(url).then(res => {
return res.json();
}).then( (data: IResponseData<U>) => {
return data;
});
}
function getArticles<U>(url: string) {
return fetch(url).then(res => {
return res.json();
}).then( (data: IResponseData<U>) => {
return data;
} )
}
~(async function(){
let user = await getUser<IResponseUserData>('');
if (user.code === 1) {
console.log(user.message);
} else {
console.log(user.data.username);
}
let articles = await getArticles<IResponseArticleData>('');
if (articles.code === 1) {
console.log(articles.message);
} else {
console.log(articles.data.id);
console.log(articles.data.author.username);
}
});
泛型类
还可以这类中使用泛型
class Queue<T> {
private items: Array<T> = [];
add(item: T) {
this.items.push(item);
}
remove(): T | undefined {
return this.items.shift();
}
}
let q1 = new Queue<string>();
q1.add('a');
q1.add('b');
let v = q1.remove();
if (v) {
v.substring(0);
}
let q2 = new Queue<Element>();
let box = document.querySelector('.box');
let div = document.querySelector('div');
box && q2.add(box);
div && q2.add(div);
let v2 = q2.remove();
if (v2) {
v2.classList.add('box')
}