TypeScript实用技巧 Nominal Typing名义类型详解

目录
  • Nominal Typing(名义类型)
    • 概念解析
    • 拓展应用
    • 在Vue中的应用

Nominal Typing(名义类型)

概念解析

意思是给一个类型附加上一个“名义”,从而防止结构类型在某些情况下由于类型结构相似而被错用。假设有如下代码:

interface Vector2D { x: number, y: number };
interface Vector3D { x: number, y: number, z: number };
function calc(vector: Vector2D): void;

const vector: Vector3D = { x: 1, y: 1, z: 1}

calc(vector) // 并没有抛出错误

看上去calc()函数应该只能传入Vector2D类型,但其实也可以传入Vector3D,因为本质上Vector3DVector2D的子集。对于calc()函数来说,传入的vector变量的类型中只要同时具有xy属性即可通过类型校验。

这种特性这在TS中被称为 Structual Typing(结构类型)。通常来说这会给我们的编码过程带来便利,但极端情况下,也可能不符合我们的预期。

假如严格规定函数只能传入Vector2D类型而不能传入Vector3D类型,那么在类型实现上,就可以使用 名义上的类型(Nominal Type),通过为原类型添加一个独有标识来区分彼此:

interface Vector2D { x: number, y: number, __type: '2d' };
interface Vector3D { x: number, y: number, z: number, __type: '3d' };
function calc(vector: Vector2D): void;

对于interface,我们可以直接为其增加标志属性,但 primitive types (原始类型) 要如何处理呢?答案是使用交叉类型,例如:

type Food = string & { _type: 'food' };
type Money = number & { _type: 'money' }

你可能会对最终类型有所疑问,但这样处理之后,他们依旧是原始类型。因为实际上原始类型最终都会解析成对应的 WrapperType (包裹类型),例如string → Stringnumber → Number,就像在JS中一样。这意味着你可把它们当做原始类型使用:

const money = 100 as Money;
const bill = money * 1; // bill 仍然是 number 类型

虽然这样的使用方式显得不太优雅,甚至有些繁琐,但在某些情况下至少可以保证类型安全。假如你的类型系统中有许多 基础类型单元,那可能会非常有用。

拓展应用

这样的类型虽然可以被当做原始类型使用,但本质上又不是纯粹的原始类型。我们可以利用这个特性,写出一些非常有趣和实用的类型

例如在字面量枚举时,我们可以在限制预设值的同时,使用 基于原始类型拓展出来的名义类型 进行兜底,从而使得我们的类型能够在具备足够自由性的前提下,仍能享受到TypeScript的类型提示,如下:

可以看到,CustomLiteral名义类型不但可以享受到Literal字面量的类型提示,又能跳出枚举的限制,使用自定义字符串。倘若我们将Literal和原始类型string直接交叉:

联合类型的机制本质上是求并集,而求并集最终得到的类型将会是更加广泛而通用的string,这使得我们反而使失去了字面量类型的推导。想要实现上述效果,就需要为string类型赋予“名义”,使它不同于普通的原始类型,不再那么“广泛”,从而在求并集的时候,不至于被string类型彻底拿捏。附上Typescript Playground

在Vue中的应用

其实在Vue3源码中也有很多 Nominal Typing 的例子,例如VNodeTeleportKeepAliveFragment等等这些内置组件,他们的定义中都有一个标志变量用于区分。分别对应了__is_VNode__isTeleport__isKeepAlive__isFragment。下图是KeepAlive组件的声明,更多组件声明可以移步官方仓库查阅。

如果类型声明的位置处在函数入参上,为了防止与用户定义的属性产生冲突,通常会采用unique symbol作为键值来构造名义类型,例如:

以上就是TypeScript实用技巧 Nominal Typing名义类型详解的详细内容,更多关于TypeScript名义类型Nominal Typing的资料请关注我们其它相关文章!

时间: 2022-09-22

详解Anyscript开发指南绕过typescript类型检查

目录 前言 场景设定 解决方法 注释忽略 场景用例 类型断言 场景用例 泛型转换 场景用例 总结 前言 随着越来越多的前端项目采用 typescript 来开发,越来越多前端开发者会接触.使用这门语言.它是前端项目工程化的一个重要帮手,结合 vscode 编辑器,给予了前端开发者更严谨.高效的编码体验.但同时,严格的类型检查也会使部分开发者的编码效率有所降低,将时间花费在解决类型冲突.类型不匹配上,从而导致望而却步,迟迟不敢上手. 本文描述了几种绕过 typescript 类型检查的方法,帮助t

TypeScript编写自动创建长度固定数组的类型工具详解

目录 前言 代码 判断 List 的长度是否等于 Len 前言 在 TypeScript 中,当需要一个长度固定的数组时,通常会想到使用元组来进行表示,不过相对于数组而言,元组的每个元素的类型都不必是一致的. 如果现在需要一个长度为 30,元素类型为 string 的数组类型,其实就是一个元组,如果直接手写出来,那也太麻烦了,本文因此有感而发,编写了自动创建的类型工具. 代码 首先,不管三七二十一,先把这个类型工具给定义出来: type FixedArray = any 然后开始逐步分析,先从泛

TypeScript 的条件类型使用示例详解

目录 TypeScript 的条件类型使用方式 条件类型和 keyof 组合 在条件返回中使用 T 在类型输出中使用 T 时的联合类型 使用条件类型推断类型 总结 TypeScript 的条件类型使用方式 我们可以使用 TypeScript 中的条件类型来根据逻辑定义某些类型,就像是在编写代码那样. 它采用的语法和我们在 JavaScript 中熟悉的三元运算符很像:condition ? ifConditionTrue : ifConditionFalse. 我们来看看他是怎么工作的. 假设我

深入了解TypeScript中常用类型的使用

目录 原始类型:string,number,boolean 数组(Arrays) Any类型 在变量上使用类型注释 函数(Functions) 返回类型注释(Return Type Annotations) 匿名函数(Anonymous Functions) 对象类型(Object Types) 可选属性(Options Properties) 联合类型(Union Types) 定义一个联合类型(Define a Union Type) 使用联合类型(Working with Union Ty

TypeScript 内置高级类型编程示例

目录 TypeScript 类型编程 TypeScript 内置高级类型 Pick<Type, Keys> Exclude<UnionType, ExcludedMembers> ReturnType<Type> 更多类型体操学习 TypeScript 类型编程 TypeScript 的类型系统,最基本的是简单对应 JavaScript 的 基本类型,比如 string.number.boolean 等,然后是新增的 tuple.enum.复合类型.交叉类型.索引类型等

直观详细的typescript隐式类型转换图文详解

正文 1.unknown是所有类型的父类型,其他类型都可以赋值给 unknown let a: undefined = undefined; let b: null = null; let x2: unknown; x2 = a; //正确 x2 = b; //正确 2.never 是任何类型的子类型,可以赋给任何类型 let a: undefined = undefined; let b: null = null; function err(): never { // OK throw new

TypeScript常见类型及应用示例讲解

目录 常见类型(Everyday Types) 原始类型: 数组(Array) any noImplicitAny 变量上的类型注解(Type Annotations on Variables) 函数(Function) 参数类型注解(Parameter Type Annotations) 返回值类型注解(Return Type Annotations) 匿名函数(Anonymous Functions) 对象类型(Object Types) 可选属性(Optional Properties)

java实现网页爬虫的示例讲解

这一篇目的就是在于网页爬虫的实现,对数据的获取,以便分析. 目录: 1.爬虫原理 2.本地文件数据提取及分析 3.单网页数据的读取 4.运用正则表达式完成超连接的连接匹配和提取 5.广度优先遍历,多网页的数据爬取 6.多线程的网页爬取 7.总结 爬虫实现原理 网络爬虫基本技术处理 网络爬虫是数据采集的一种方法,实际项目开发中,通过爬虫做数据采集一般只有以下几种情况: 1) 搜索引擎 2) 竞品调研 3) 舆情监控 4) 市场分析 网络爬虫的整体执行流程: 1) 确定一个(多个)种子网页 2) 进

hadoop中实现java网络爬虫(示例讲解)

这一篇网络爬虫的实现就要联系上大数据了.在前两篇java实现网络爬虫和heritrix实现网络爬虫的基础上,这一次是要完整的做一次数据的收集.数据上传.数据分析.数据结果读取.数据可视化. 需要用到 Cygwin:一个在windows平台上运行的类UNIX模拟环境,直接网上搜索下载,并且安装: Hadoop:配置Hadoop环境,实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS,用来将收集的数据直接上传保存到HDFS,然后用MapReduce

vue实现微信分享朋友圈,发送朋友的示例讲解

首先下载微信jssdk引入项目中,这里我就不说怎么去安装了,如果不会的可以看一下npm教程和es6的教程. 第一步,引入微信jssdk,此处我是通过下载微信jssdk,然后用webpack引入进项目的. 第二步,获取详情数据,渲染页面. 第三步,获取详情数据成功后再获取微信签名,token等配置信息. 第四步,通过api配置所想要的功能 代码: <template> <div class="details"> <player :videoUrl="

python实现校园网自动登录的示例讲解

因为最近想用树莓派搞个远程监控系统,又因为学校的网需要从网页登录而树莓派又不方便搞个显示器带着,所以寻思着搞个能够自动登录校园网的脚本程序,省去了每次都要打开浏览器输入账号密码的烦恼. 1.工具 火狐浏览器+firedebug插件,debug插件可才浏览器中附加组件中添加,其他浏览器也可以只要可以监控浏览器的网络行为即可. python+requests包 2.步骤 1)  先打开到登录界面,然后在按f12打开firedebug插件,此时debug无任何记录行为,然后点击刷新按钮,再点击登录按钮

对python插入数据库和生成插入sql的示例讲解

如下所示: #-*- encoding:utf-8 -*- import csv import sys,os import pymysql def read_csv(filename): ''' 读取csv文件 ''' data = [] with open(filename) as f: f_csv = csv.reader(f) headers = next(f_csv) #数据格式[1111,22222,1111,1111,.....] for row in f_csv: # Proces

对Pandas DataFrame缺失值的查找与填充示例讲解

查看DataFrame中每一列是否存在空值: temp = data.isnull().any() #列中是否存在空值 print(type(temp)) print(temp) 结果如下,返回结果类型是Series,列中不存在空值则对应值为False: <class 'pandas.core.series.Series'> eventid False iyear False imonth False iday False approxdate True extended False reso

NPOI实现两级分组合并功能(示例讲解)

NPOI版本:2.2.1.0 最近公司有这样的需求: 统计每个部门下面,多个费用使用情况.部门存在多级,但统计时,只需统计到2级,2级以下的,归到第2级的部门下.并且要求,第2级部门有个小计,第1级部门需要有个合计.最后,还需提供总计. 本来对NPOI研究的还不够深入的,以前都是直接通过别人提供的代码来实现对DataTable中的数据进行全部导出,但里面不带合并,及合计功能,不满足当前需求.不得已,只有好好地研究一下了.还好,最终实现了要求. 在此,也感谢其他提供相关资料的人员,让我实现了此功能

对python创建及引用动态变量名的示例讲解

实际上在python中用列表就可以实现动态变量名的管理,python中的列表中可以存储任何类型的元素: listA = [0,"str",B()] 上述列表分别存储了整数,字符串,对象.使用和创建时只需配合列表下标即可. 但python确实有创建动态表量名的方法: names = locals() for i in range(1,10): names['a%i'%i] = input('Abss %d'%i) for i in range(1,10): print(names['a%

Python计算IV值的示例讲解

在对变量分箱后,需要计算变量的重要性,IV是评估变量区分度或重要性的统计量之一,python计算IV值的代码如下: def CalcIV(Xvar, Yvar): N_0 = np.sum(Yvar==0) N_1 = np.sum(Yvar==1) N_0_group = np.zeros(np.unique(Xvar).shape) N_1_group = np.zeros(np.unique(Xvar).shape) for i in range(len(np.unique(Xvar)))

你不知道的 TypeScript 高级类型(小结)

前言 对于有 JavaScript 基础的同学来说,入门 TypeScript 其实很容易,只需要简单掌握其基础的类型系统就可以逐步将 JS 应用过渡到 TS 应用. // js const double = (num) => 2 * num // ts const double = (num: number): number => 2 * num 然而,当应用越来越复杂,我们很容易把一些变量设置为 any 类型,TypeScript 写着写着也就成了 AnyScript.为了让大家能更加深入