您的位置:首页 > Web前端 > JavaScript

TypeScript 学习笔记7: Generics

2017-07-03 16:54 393 查看
原文链接:https://leanpub.com/essentialtypescript/read#leanpub-auto-generics

在静态语言中,如C++、C#、Java,generic 是为了让代码具备一定的动态类型,以便于减少重复性。而Javascript本身就是动态类型语言,为什么还需要generic呢?我想,是为了增加可读性,同时增加“静态性”,给编译器提供一些类型信息,让它给我们提供一些限制,以免代码写得过于随意了。

1. generic functions

function clone(value) {
let serialized = JSON.stringify(value);
return JSON.parse(serialized);
}


这是js中典型的clone函数,这个函数的输入和输出应该是同一种类型的对象。如何保证这一点呢?使用 Generic:

function clone<T>(value: T) {
let serialized = JSON.stringify(value);
return JSON.parse(serialized);
}


说明:

1. 只是第一行有变化,在函数名和参数列表之间加了 <T>, 给参数指定类型 T;

2. T 只是一种惯用法,可以写任何字符串,只要符合变量命名规则即可。

当调用这个函数时,鼠标悬停在函数名上,可以看到TypeScript识别出来的类型:



2. generic classes

在js中,Array 本身就是generic类型的:

var nums: number[] = [1,2];

var nums: Array<Number> = [1,2];


这两种写法完全等价。

定义一个 generic 的键值对class:

class KeyValuePair<TKey, TValue> {
constructor(public key: TKey,
public value: TValue) {
}
}

let pair1 = new KeyValuePair(1, 'First');
let pair2 = new KeyValuePair('Second', Date.now());
let pair3 = new KeyValuePair(3, 'Third');


把鼠标悬停在任意变量上,TypeScript 都能识别出类型,如:



我们也可以明确指定 Key 和Value的类型:

let pair1 = new KeyValuePair<number, string>(1, 'First');
let pair2 = new KeyValuePair<string, Date>('Second', Date.now());
let pair3 = new KeyValuePair<number, string>(3, 'Third');


如果构造函数传入的数值与指定的类型不同,编译器会报错。这时,在Visual Studio Code中,我们会看到 pair2 的 Date.now() 下面有错误提示。因为,Date.now() 的返回值时number,不是Date。

在更复杂的情况下,TypeScript 也可以推断出类型信息,例如:

class KeyValuePairPrinter<T,U> {
constructor(private pairs: KeyValuePair<T,U>[]) {
}

print() {
for(let p of this.pairs) {
console.log(`${p.key}: ${p.value}`);
}
}
}

let printer = new KeyValuePairPrinter([pair1, pair2, pair3]);
printer.print();


第12行有编译错误,因为,pair2 和 其它两个变量类型不同。

3. generic constraints

回顾一下这个函数:

function totalLength(x: {length: number}, y:{length: number}) {
var total: number = x.length + y.length;
return total;
}


看起来挺完美,但我们无法避免这种情况:

var length = totalLength('Jess', [1,2,3]);


把字符串和数组的length相加没有什么意义。

有了generic,可以这么改:

function totalLength<T>(x: T, y: T) {
var total: number = x.length + y.length;
return total;
}


这样可以保证两个参数类型相同,但,又不能保证它们都有 length 属性。

Generic constraints 来了:

function totalLength<T extends { length: number }>(x: T, y: T) {
var total: number = x.length + y.length;
return total;
}


说明:

1. extends 关键字,前面是T,后面是一个匿名 interface;

2. 我们曾经见过extends,在类的 “继承” 时用过它;

这里可以用任何interface,不必是匿名的,如:

interface IHaveALength {
length: number
}

function totalLength<T extends IHaveALength>(x: T, y: T) {
var total: number = x.length + y.length;
return total;
}


这样,TypeScript可以明确的识别出下面的代码是否合法:

var l1 = totalLength([1,2], [1,2,3]);
var l2 = totalLength('Less', [1,2,3]);


Generic 也兼容子类型,例如,我们定义一个Array的子类:

class CustomArray<T> extends Array<T> {
toJson(): string {
return JSON.stringify(this);
}
}


这种用法是合法的:

var length = totalLength([1,2], new CustomArray<number>());
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  typescript javascript