前言
模模糊糊使用Ts进行开发一年多了,在这一年的使用中个人常常把Ts当作一门简单的工具作为使用。直到这段时间,挖掘了Ts类型编程更为深入的部分,才知道自己已经停滞不前了。
通过这篇文章,你将会
- 深入理解泛型
- 认识类型断言
- …
泛型
软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
在TypeScript中泛型就承担着代替未来传入类型的责任,也可以把他看作一个能够作为类型参数变量。
泛型之Hello World
下面来创建第一个使用泛型的例子:identity函数。 并且这个函数会返回任何传入它的值。
如果不用泛型的话,这个函数可能是下面这样:
function identity(arg: number): number {
return arg;
}
如上,如果使用基本变量的话。那么只能规定一种变量来作为值类型。
或者,我们使用any
类型来定义函数:
function identity(arg: any): any {
return arg;
}
使用any
类型会导致这个函数可以接收任何类型的arg
参数,但是这样就不符合我们的本意:传入的类型与返回的类型应该是相同的。
比如我们传入一个数字,我们只知道任何类型的值都有可能被返回,而不能确定这个值是不是数字。
因此,我们需要一种方法使返回值的类型与传入参数的类型是相同的。 这里,我们使用了 类型变量,它是一种特殊的变量,只用于表示类型而不是值。👇
function identity<T>(arg: T): T {
return arg;
}
我们给identity
函数添加了类型变量T
。 T
帮助我们捕获用户传入的类型(比如:number
),相当于给T这个变量赋值以后他就是一个常量值了。之后我们就可以使用这个类型,比如我们再次使用了 T
当做返回值类型。
通过使用变量T
,我们就成功的规定了入出类型值统一的函数。
在这个函数中,我们就变量T
,就称为了泛型。
我们定义了泛型函数后,可以用两种方法使用。 第一种是,传入所有的参数,包含类型参数:
let output = identity<string>("myString"); // type of output will be 'string'
这里我们明确的指定了T
是string
类型,并做为一个参数传给函数,使用了<>
括起来而不是()
。
第二种方法更普遍。利用了类型推论 – 即编译器会根据传入的参数自动地帮助我们确定T的类型:
let output = identity("myString"); // type of output will be 'string'
注意我们没必要使用尖括号(<>
)来明确地传入类型;编译器可以查看myString
的值,然后把T
设置为它的类型。 类型推论帮助我们保持代码精简和高可读性。如果编译器不能够自动地推断出类型的话,只能像上面那样明确的传入T的类型,在一些复杂的情况下,这是可能出现的。
使用泛型变量
接着identity泛型函数的例子,假设此时我们想打印出arg的长度:
function loggingIdentity<T>(arg: T): T {
console.log(arg.length); // Error: T doesn't have .length
return arg;
}
如果这么直接打印的话,犹豫我们此处定义的泛型T类型实际等于任意类型。编译器不能保证任意类型都具有length属性,例如number类型,所以就报错了。
现在假设我们想操作T
类型的数组而不直接是T
。由于我们操作的是数组,所以.length
属性是应该存在的。 我们可以像创建其它数组一样创建这个数组:
function loggingIdentity<T>(arg: T[