TypeScript——泛型理论与实践

1. 简介

软件工程的一个重要部分就是构建组件,组件不仅需要有定义良好和一致的
API,还需要是可复用的。好的组件不仅能够兼容现有的数据类型,也能适用于未来可能出现的数据类型,这在构建大型软件系统时会有很大的灵活度以及很高的扩展性。
在比如 C# 和 Java
语言中,用来创建可复用组件的工具,我们称之为泛型。利用泛型,我们可以创建一个支持众多类型的组件,并且用户在使用组件时可以传入自己想要的类型。

2. 举个🌰

上述的简介还是不够通俗,我们接下来结合一些简单代码来理解什么是泛型:

function getVal(val: string): string {return val
}const foo = getVal('123')const bar = getVal(123)

在这里插入图片描述

我们定义了一个函数 getVal,指定接收一个类型为 string 的参数,并将入参作为返回值。
结合代码图示,我们老老实实传入 string 类型的值后,可以看到代码正常,并且 TypeScript 很智能的给出编码提示。在我们传入 number 类型的值时也会很及时的给出报错信息,这很好、很强大!
但是,如果我们编写的方法就是需要接收多种类型的参数,那该如何呢?🤔
小明同学不假思索的写出以下代码:

function getVal(val: string | number): string | number {return val
}const foo = getVal(123)

通过联合类型(Union Types)实现了 getVal 函数接收多种类型参数的需求,并且运行完美没有任何报错。只是由于返回值也是一个联合类型,所以 TypeScript 并不能准确知道函数具体的返回值类型。因此导致 TypeScript 给出的代码提示是两种类型值的共有方法:

在这里插入图片描述

我们想要实现的函数功能是:接收任意类型的入参,返回值等同于入参的类型,这很明显不符合我们的预期。

3. 泛型——函数

我们可以使用泛型(Generics)来实现上面的函数功能:

function getVal<T>(val: T): T {return val
}const foo = getVal(123)const bar = getVal('123')

上面示例中,函数 getVal() 的函数名后面尖括号的部分 <T>,就是泛型的类型参数,参数要放在一对尖括号 <> 里面。
泛型的类型参数本质就是一个接收 TypeScript类型,它和 js 中函数定义的参数(形参)极为类似,只不过在 js 中形参接收的是值,在 TypeScript 中类型参数接收的是类型。

在这里插入图片描述

在这里插入图片描述

通过使用类型参数接收指定类型,我们精准的得到函数返回值类型,并享受更好的智能提示。
💡大多数时候我们不必显式的传入类型参数,因为 TypeScript 可以根据传入的值推断出它的类型,所以更推荐下面的写法:

function getVal<T>(val: T): T {return val
}const foo = getVal(123)

在这里插入图片描述

4. 泛型——接口

通过上面的内容,我们已经感受到泛型很方便、灵活,然而它的强大之处才体现出冰山一角。

  1. 使用 interface 描述一个对象时,动态定义某个属性的类型
interface Data<T> {name: string;val: T;
}const foo: Data<number> = {name: "tom",val: 123,
};const bar: Data<string> = {name: "tom",val: "123",
};
  1. 使用 interface 定义一个泛型函数的类型
function id<Type>(arg: Type): Type {return arg;
}let myIdmyId = id

在这里插入图片描述

普通函数的类型好定义 type Fn = (arg: any) => void ,泛型函数的类型如何定义呢?
其实编辑器也是可以给出答案的:

在这里插入图片描述

粘一下代码即可:

interface Fn {<Type>(arg: Type): Type
}function id<Type>(arg: Type): Type {return arg;
}let myId: FnmyId = id // ✔️myId = 123 // ❌

在这里插入图片描述

5. 泛型——类

泛型类的类型参数写在类名后面:

class Person<Name, Age> {name: Name;age: Age;constructor(name: Name, age: Age) {this.name = name;this.age = age;}
}new Person<string, number>("Bob", 88);

下面是继承泛型类的例子:

class Person<Name, Age> {name: Name;age: Age;constructor(name: Name, age: Age) {this.name = name;this.age = age;}
}class Teacher extends Person<string, number> {gender: string = 'male'
}

上面示例中, Person 类有两个类型参数,分别是 NameAge,使用时必须给出 NameAge 的类型,所以 Teacher 类继承时按照需要把类型参数写进去就行了 Person<string, number>
但是如果我们想在初始化 Teacher 类的时候动态传入 Person 类的类型参数,那该如何呢?🤔🤔
写法应该是一样的,我们可以尝试写出以下代码:

class Person<Name, Age> {name: Name;age: Age;constructor(name: Name, age: Age) {this.name = name;this.age = age;}
}class Teacher<Name, Age, Gender> extends Person<Name, Age> {gender: Gender;constructor(name: Name, age: Age, gender: Gender) {super(name, age);this.gender = gender;}
}type Gender = "male" | "woman";const teacher = new Teacher<string, number, Gender>("Bob", 88, "male");

我们可以看到,类型正常运作,结果也正常:

在这里插入图片描述

6. 泛型——类型别名

相较于接口、类中的泛型运用,在类型别名中泛型的使用可能更常见一点:

type CustomUnion<T> = T | string

以上是泛型的简单应用,通过动态的传入类型,便拥有了一个自定义的联合类型。
下面是定义树形结构的例子:

type Tree<T> = {value: T;children: Tree<T>[];
};const tree: Tree<string> = {value: "tree-1",children: [{value: "tree-1-1",children: [],},],
};

在类型别名内部递归引用自身时,传入泛型,依然能够很好的工作:

在这里插入图片描述

7. 泛型——类型参数默认值

我们在上面讲到 “类型参数极其类似 js 中函数的形参”,在 ES6 中函数的形参可以定义默认值,这很优雅,非常方便。在泛型中定义默认类型同样非常类似:

// 定义类型参数默认类型
function getVal<T = string>(val: T): T {return val
}

上面示例中,T = string 表示类型参数的默认值是 string。调用 getVal() 时,如果不给出 T 的值,TypeScript 就认为 T 等于 string
但是,因为 TypeScript 会从实际参数推断出 T 的值,从而覆盖掉默认值,所以下面的代码不会报错。

getVal(true) //✔️✔️

在这里插入图片描述

8. 泛型——类型约束

很多时候类型参数并不是无限制的,我们也可以对于传入的类型制定约束条件。

type ButtonType = 'default' | 'primary'function getButtonType<T>(type: T): T {return type
}getButtonType('123')

就像这个🌰,我们传入 '123' 后运行很稳定、不报错,但是很明显 getButtonType 的参数不能随便传。
根据代码块内容,它只能接收 'default''primary' 这两个参数,我们如何做到呢?🤔🤔🤔
这种情况下真乃是泛型约束的绝佳应用场景!泛型约束的语法尤为简单,像 T extends string 这样。
在 TypeScript中,T extends string 是一个类型限界表达式,它表示类型参数 T 必须是一个字符串类型或字符串类型的子类型。我们来轻微改写一下:

type ButtonType = 'default' | 'primary'function getButtonType<T extends ButtonType>(type: T): T {return type
}

这样改过之后,不仅可以准确的给出报错提示,

在这里插入图片描述

还能给我们提供便捷的智能提示,这对于团队协作的可靠性及高效率也有着很大的提升!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/108552.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Appium混合页面点击方法tap的使用

原生应用开发&#xff0c;是在Android、IOS等移动平台上利用官方提供的开发语言、开发类库、开发工具进行App开发&#xff1b;HTML5&#xff08;h5&#xff09;应用开发&#xff0c;是利用Web技术进行的App开发。目前&#xff0c;市面上很多app都是原生和h5混合开发&#xff0c…

ChatGPT帮助一名儿童确诊病因,之前17位医生无法确诊

9月13日&#xff0c;Today消息&#xff0c;一位名叫Alex的4岁儿童得了一种浑身疼痛的怪病&#xff0c;每天需要服用Motrin&#xff08;美林&#xff09;才能止痛。3年的时间&#xff0c;看了17名医生无法确诊病因。&#xff08;新闻地址&#xff1a;https://www.today.com/heal…

拼多多面试题解析:Java实现继承的七种方式!

大家好&#xff0c;我是小米&#xff01;今天&#xff0c;我要和大家一起来深入探讨一下拼多多的面试题&#xff1a;Java 实现继承有哪 7 种方式&#xff1f;这是一个相当有深度的问题&#xff0c;不过别担心&#xff0c;我会尽力以通俗易懂的方式给大家讲解清楚&#xff0c;让…

c语言练习57:浮点数在内存中的存储

浮点数在内存中的存储 上⾯的代码中&#xff0c; num 和 *pFloat 在内存中明明是同⼀个数&#xff0c;为什么浮点数和整数的解读结果会差别 这么⼤&#xff1f; 要理解这个结果&#xff0c;⼀定要搞懂浮点数在计算机内部的表⽰⽅法。 根据国际标准IEEE&#xff08;电⽓和电⼦⼯…

《算法竞赛·快冲300题》每日一题:“点灯游戏”

《算法竞赛快冲300题》将于2024年出版&#xff0c;是《算法竞赛》的辅助练习册。 所有题目放在自建的OJ New Online Judge。 用C/C、Java、Python三种语言给出代码&#xff0c;以中低档题为主&#xff0c;适合入门、进阶。 文章目录 题目描述题解C代码Java代码Python代码 “ 点…

OpenCV Series : Target Box Outline Border

角点 P1 [0] (255, 000, 000) P2 [1] (000, 255, 000) P3 [2] (000, 000, 255) P4 [3] (000, 000, 000)垂直矩形框 rect cv2.minAreaRect(cnt)targetColor roi_colortargetThickness 1targetColor (255, 255, 255)if lineVerbose:if …

Docker基础学习

Docker 学习目标&#xff1a; 掌握Docker基础知识&#xff0c;能够理解Docker镜像与容器的概念 完成Docker安装与启动 掌握Docker镜像与容器相关命令 掌握Tomcat Nginx 等软件的常用应用的安装 掌握docker迁移与备份相关命令 能够运用Dockerfile编写创建容器的脚本 能够…

【漏洞复现】AspCMS commentList.asp SQL注入

漏洞描述 AspCMS commentList.asp 存在SQL注入漏洞&#xff0c;攻击者通过漏洞可以获取管理员md5的密码&#xff0c;进行解密后登录获取敏感数据。 免责声明 技术文章仅供参考&#xff0c;任何个人和组织使用网络应当遵守宪法法律&#xff0c;遵守公共秩序&#xff0c;尊重…

SpringBoot-线程池ThreadPoolExecutor异步处理(包含拆分集合工具类)

ThreadPoolExecutor VS ThreadPoolTaskExecutor ThreadPoolTaskExecutor是对ThreadPoolExecutor进行了封装处理。 配置文件application.yml # 异步线程配置 自定义使用参数 async:executor:thread:core_pool_size: 10max_pool_size: 100 # 配置最大线程数queue_capacity: …

R拒绝访问的解决方案

Win11系统 安装rms的时候报错&#xff1a; Error in loadNamespace(j <- i[[1L]], c(lib.loc, .libPaths()), versionCheck vI[[j]]) : namespace Matrix 1.5-4.1 is already loaded, but > 1.6.0 is required## 安装rms的时候报错&#xff0c;显示Matrix的版本太低…

【PCL-11】提取平面上层的目标物,剔除平面下层目标物

因项目需求&#xff0c;需提取平面上的物体&#xff0c;不提取平面下的物体&#xff0c;尝试采用超体聚类LCCP分割的方式&#xff0c;但由于上层点云模型一侧有空洞&#xff0c;导致分割效果不理想。 这里采用pcl::ExtractPolygonalPrismData类&#xff0c;实现平面上物体的提取…

免费:CAD批量转PDF工具,附下载地址

分享一款CAD 批量转PDF、打印的工具插件。能自动识别图框大小、自动识别比例、自动编号命名。重点&#xff01;重点&#xff01;重点&#xff01;自动将CAD的多张图纸一次性地、批量地转为PDF&#xff0c;或者打印。效果看下图&#xff1a; 适用环境&#xff1a; 32位系统 Auto…