前言
TypeScript真香系列的内容将参考中文文档,但是文中的例子基本不会和文档中的例子重复,对于一些地方也会深入研究。另外,文中一些例子的结果都是在代码没有错误后编译为JavaScript得到的。如果想实际看看TypeScript编译为JavaScript的代码,可以访问TypeScript的在线编译地址,动手操作,印象更加深刻。
概念
TypeScript的泛型与其他面向对象的语言中的定义是相似的。泛型可以理解为在我们定义函数、接口或者类的时候,不预先指定其相关的类型,而是在使用的时候手动指定类型。这和TypeScript基础类型中的any是有区别的。
当我们不使用泛型的时候,可能就如下这样:
function show(name: string): string {
return name;
}
function show(name: any): any {
return name;
}
上面例子中用any或许没有什么问题,但是当我们想传入的参数类型和返回值类型相同时,使用any时的返回值就可能有多种情况了。还有就是我们平时也会考虑重用性,提取公共组件等。这时泛型就能派上了。下面是一个泛型函数的例子:
function show<T>(arg: T): T {
return arg;
}
这个show函数后面的 T ,叫做类型变量,这个我们后面会介绍。这个函数我们可以用两种方法调用。第一种为传入参数,包括类型参数:
show<string>("james"); //"james"
第二种为不包括类型参数,这种方式利用了类型推论,这个再后面的章节会讲到:
show(1); //1
泛型变量
上面所写的例子中,我们可以在“<>”中指定一个变量T,并且我们要把这个T定义在参数和返回值上。编译器在检查我们的代码的时候,我们必须在函数体中正确的使用这个通用的类型。
function show<T>(arg: T): T {
console.log(arg.length) //错误,类型T不存在length属性
return arg;
}
上面的例子中,我们在打印arg的length中报错了。原因是我们在使用这个函数的时候,传入的参数可能为数字,而数字是没有length属性的。当然,我们在调用函数的时候也要正确调用:
function show<T>(arg: T): T {
return arg;
}
show<string>(1); //错误,参数1的类型不是string
show<>(1); //错误,参数类型不能为空
针对数组:
function show<T>(arg: T[]): T[] {
return arg;
}
show<string>(["奥","利","给"]); //["奥", "利", "给"]
function show<T>(arg: Array<T>): Array<T> {
return arg;
}
show<number>([6, 6, 6]); // [6, 6, 6]
泛型接口
如下所示:
interface IMan {
<T>(arg: T): T
}
function man<T>(arg: T): T{
return arg;
}
let showMan: IMan = man;
showMan("james"); //"james"
还有另外一种写法,这种方法我们可以把泛型参数当做整个接口的参数:
interface IMan<T> {
(arg: T): T
}
function man<T>(arg: T): T{
return arg;
}
let showMan: IMan<string> = man;
showMan("james"); //"james"
这样的好处就是在使用时知道具体的参数类型了。
泛型类
我们可以使用(<>)和尖括号中间的变量,然后跟在类名的后面来表示泛型类。需要注意的是,泛型类指的是实例部分的类型,类的静态属性不能使用泛型类型。
class Man<T> {
name: T;
constructor(arg: T) {
this.name = arg;
}
play(a: T) : T {
return a;
}
}
let showMan = new Man<string>("James");
showMan.name; //"James"
showMan.play("ball"); //"ball"
也可以稍微复杂一点:
class Man<T> {
name: T;
sum: T[];
constructor(arg: T) {
this.name = arg;
this.sum = [];
}
push(item: T): void{
this.sum.push(item)
}
output() {
return this.sum;
}
}
let showMan = new Man<string>("James");
showMan.push("one for all");
showMan.output(); //["one for all"]
泛型约束
让我们回到泛型变量一节中的一个例子:
function show<T>(arg: T): T {
console.log(arg.length) //错误,类型T不存在length属性
return arg;
}
如果我们想要使用length属性,那该怎么办?可以看下面的例子:
interface ILen {
length: number;
}
function show<T extends ILen>(arg: T): T {
console.log(arg.length) //没有报错了
return arg;
}
虽然上面的例子没有再报错,但是因为这个泛型函数被定义了约束,所以我们再调用函数的时候又有了讲究,也就是我们在传值的时候需要像下面的例子一样:
interface ILen {
length: number;
}
function show<T extends ILen>(arg: T): T {
console.log(arg.length)
return arg;
}
show(3); //错误,参数3不能分配给类型ILen
show({ length: 2 }); // 2 {length: 2}
show({length:2, arg:1}) //2 {length: 2, arg: 1}
show({length:2, arg:"james"}) //2 {length: 2, arg: "james"}
参考
https://github.com/zhongsp/TypeScript
最后
文中有些地方可能会加入一些自己的理解,若有不准确或错误的地方,欢迎指出~
原文始发于微信公众号(旅行中的程序员):TypeScript真香系列-泛型
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/217332.html