Typescript入门手册之泛型
Posted 余光、
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Typescript入门手册之泛型相关的知识,希望对你有一定的参考价值。
【Typescript入门手册】之泛型
🚀【TypeScript入门手册】记录了出场率较高的Ts概念,旨在帮助大家了解并熟悉Ts
🎉 本系列会持续更新并更正,重点关照大家感兴趣的点,欢迎同学留言交流,在进阶之路上,共勉!
👍 star本项目给作者一点鼓励吧
📚 系列文章,收藏 不走丢哦
一、泛型(Generics)
软件工程的一个重要部分就是构建组件,组件不仅需要有定义良好和一致的API,也需要是可复用的(reusable)。好的组件不仅能够兼容今天的数据类型,也能适用于未来可能出现的数据类型,我们可以创建一个支持众多类型的组件,这让用户可以使用自己的类型消费(consume)这些组件。
让我们开始写第一个泛型,一个恒等函数(identity function)。所谓恒等函数,就是一个返回任何传进内容的函数。你也可以把它理解为类似于 echo 命令。
function identity(arg: number): number
sreturn arg;
我们需要一种可以捕获参数类型的方式,然后再用它表示返回值的类型。这里我们用了一个类型变量(type variable),一种用在类型而非值上的特殊的变量。
function identity<Type>(arg: Type): Type
return arg;
现在我们已经给恒等函数加上了一个类型变量Type
,这个Type
允许我们捕获用户提供的类型,使得我们在接下来可以使用这个类型。这里,我们再次用Type
作为返回的值的类型。在现在的写法里,我们可以清楚的知道参数和返回值的类型是同一个。
在我们写了一个泛型恒等函数后,我们有两种方式可以调用它。第一种方式是传入所有的参数,包括类型参数:
let output = identity<string>("myString");
第二种方式可能更常见一些,这里我们使用了类型参数推断(type argument inference)我们希望编译器能基于我们传入的参数自动推断和设置Type
的值。
let strA = identity('isString'); // let strA: string
注意这次我们并没有用<Type>
明确的传入类型,当编译器看到isString
这个值,就会自动设置Type
为它的类型(即 string)。
类型参数推断是一个很有用的工具,它可以让我们的代码更短更易阅读。而在一些更加复杂的例子中,当编译器推断类型失败,你才需要像上一个例子中那样,明确的传入参数。
二、使用泛型类型变量(Working with Generic Type Variables)
我们在上一节了解到的例子中,Type是直接作为类型进行传递的,而在某些时候Type也可以是类型的某一个参数,例如:
function loggingIdentity<Type>(arg: Type[]): Type[]
console.log(arg.length);
console.log(arg[0]);
return arg;
你可以这样理解loggingIdentity
的类型:泛型函数loggingIdentity
接受一个 Type 类型参数和一个实参arg
,实参arg
是一个Type
类型的数组。而该函数返回一个Type
类型的数组。
现在我们使用类型变量 Type,是作为我们使用的类型的一部分,而不是之前的一整个类型,这会给我们更大的自由度。
三、泛型类型 (Generic Types)
泛型函数的形式就跟其他非泛型函数的一样,都需要先列一个类型参数列表,这有点像函数声明:
function identity<Type>(arg: Type): Type
console.log('>>:', arg);
return arg;
let myIdentity: <T>(arg: T) => T = identity;
注意:不要对<T>
感到疑惑,泛型的类型参数(Type || T || XX)可以使用不同的名字,只要数量和使用方式上一致即可。
我们也可以以对象类型的调用签名的形式,书写这个泛型类型:
function identity<Type>(arg: Type): Type
return arg;
let myIdentity: <Type>(arg: Type): Type = identity;
这可以引导我们写出第一个泛型接口,让我们使用上个例子中的对象字面量,然后把它的代码移动到接口里:
interface GenericIdentityFn
<Type>(arg: Type): Type;
function identity<Type>(arg: Type): Type
return arg;
let myIdentity: GenericIdentityFn = identity;
四、泛型类(Generic Classes)
泛型类写法上类似于泛型接口。在类名后面,使用尖括号中 <> 包裹住类型参数列表:
class GenericNumber<NumType>
zeroValue: NumType;
add: (x: NumType, y: NumType) => NumType;
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y)
return x + y;
;
在这个例子中,并没有限制你只能使用number
类型。我们也可以使用其他类型:
type strOrNum = string | number;
class GenericNumber<NumType>
zeroValue: NumType;
log: (x: NumType, y: NumType) => NumType;
let myGenericNumber = new GenericNumber<strOrNum>();
myGenericNumber.zeroValue = 0;
myGenericNumber.log = function(x, y)
console.log(x, y);
return `$x和$y`;
// (parameter) x: strOrNum
// (parameter) y: strOrNum
;
就像接口一样,把类型参数放在类上,可以确保类中的所有属性都使用了相同的类型。
五、泛型约束(Generic Constraints)
在早一点的 第三章函数中 我们想要获取参数obj.length
属性,但是编译器并不能证明每种类型都有 .length
属性,相比于能兼容任何类型,我们更愿意约束这个函数,让它能使用带有.length
属性的类型。只要类型有这个成员,我们就允许使用它,但必须至少要有这个成员。
function longest<Type extends length: number >(a: Type, b: Type)
if (a.length >= b.length)
return a;
else
return b;
我们也可以列出对 Type 约束中的必要条件,为此,我们需要创建一个接口,用来描述约束。这里,我们创建了一个只有 .length 属性的接口,然后我们使用这个接口和 extend关键词实现了约束:
interface Lengthwise
length: number;
function longest<Type extends Lengthwise>(a: Type, b: Type)
if (a.length >= b.length)
return a;
else
return b;
5.1 在泛型约束中使用类型参数(Using Type Parameters in Generic Constraints)
你可以声明一个类型参数,这个类型参数被其他类型参数约束。
举个例子,我们希望获取名的值,一个对象给定属性为此,我们需要确保我们不会获取obj
上不存在的属性。所以我们在两个类型之间建立一个约束:
function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key)
return obj[key];
let x = a: 1, b: 2, c: 3, d: 4 ;
getProperty(x, "a");
getProperty(x, "m");
// 类型“"m"”的参数不能赋给类型“"a" | "b" | "c" | "d"”的参数。
5.2 在泛型中使用类类型(Using Class Types in Generics)
在 TypeScript 中,当使用工厂模式创建实例的时候,有必要通过他们的构造函数推断出类的类型,举个例子:
function create<Type>(c: new (): Type ): Type
return new c();
class BeeKeeper
hasMask: boolean = true;
class ZooKeeper
nametag: string = "Mikle";
class Animal
numLegs: number = 4;
class Bee extends Animal
keeper: BeeKeeper = new BeeKeeper();
class Lion extends Animal
keeper: ZooKeeper = new ZooKeeper();
function createInstance<A extends Animal>(c: new () => A): A
return new c();
createInstance(Lion).keeper.nametag;
createInstance(Bee).keeper.hasMask;
写在最后
本篇文章是《Typescript基础入门》第五篇,其实这篇文章已经需要花费更多的精力才能“了解”了,不妨多读几遍,记得star哦~
参考:
关于我:
- 花名:余光
- 邮箱:webbj97@163.com
- csdn:传送门
其他沉淀:
以上是关于Typescript入门手册之泛型的主要内容,如果未能解决你的问题,请参考以下文章