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


Posted in Javascript onSeptember 23, 2022

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的类型提示,如下:

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

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

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

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

在Vue中的应用

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

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

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

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

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

Javascript 相关文章推荐
解决jquery中美元符号命名冲突问题
Jan 08 Javascript
使用时间戳解决ie缓存的问题
Aug 20 Javascript
jQuery通过点击行来删除HTML表格行的实现示例
Sep 10 Javascript
JavaScript模拟重力状态下抛物运动的方法
Mar 03 Javascript
JS+CSS实现的拖动分页效果实例
May 11 Javascript
输入法的回车与消息发送快捷键回车的冲突解决方法
Aug 09 Javascript
jquery自定义插件结合baiduTemplate.js实现异步刷新(附源码)
Dec 22 Javascript
详解angular中通过$location获取路径(参数)的写法
Mar 21 Javascript
在vue中v-bind使用三目运算符绑定class的实例
Sep 29 Javascript
Vue使用lodop实现打印小结
Jul 06 Javascript
js实现简单放大镜效果
Mar 07 Javascript
v-slot和slot、slot-scope之间相互替换实例
Sep 04 Javascript
Moment的feature导致线上bug解决分析
Sep 23 #Javascript
js 实现Material UI点击涟漪效果示例
Sep 23 #Javascript
js 实现验证码输入框示例详解
Sep 23 #Javascript
TypeScript 内置高级类型编程示例
Sep 23 #Javascript
详解Anyscript开发指南绕过typescript类型检查
Sep 23 #Javascript
js基于div丝滑实现贝塞尔曲线
Sep 23 #Javascript
TS 类型兼容教程示例详解
Sep 23 #Javascript
You might like
PHP实现删除非站内外部链接实例代码
2014/06/17 PHP
php使用APC实现实时上传进度条功能
2015/10/26 PHP
使用ltrace工具跟踪PHP库函数调用的方法
2016/04/25 PHP
yii2实现分页,带搜索的分页功能示例
2017/01/07 PHP
php7 参数、整形及字符串处理机制修改实例分析
2020/05/25 PHP
Javascript实现CheckBox的全选与取消全选的代码
2010/07/20 Javascript
js this函数调用无需再次抓获id,name或标签名
2014/03/03 Javascript
JavaScript常用的弹出广告及背投广告实现方法
2015/02/06 Javascript
JavaScript计算某一天是星期几的方法
2015/08/05 Javascript
Js 获取当前函数参数对象的实现代码
2016/06/20 Javascript
easyUI下拉列表点击事件使用方法
2017/05/18 Javascript
js编写简单的计时器功能
2017/07/15 Javascript
微信小程序自定义组件之可清除的input组件
2018/07/17 Javascript
使用webpack构建应用的方法步骤
2019/03/04 Javascript
小程序中设置缓存过期的实现方法
2020/01/14 Javascript
Python读写配置文件的方法
2015/06/03 Python
使用pandas批量处理矢量化字符串的实例讲解
2018/07/10 Python
详解如何用django实现redirect的几种方法总结
2018/11/22 Python
python requests.post带head和body的实例
2019/01/02 Python
Python实现定制自动化业务流量报表周报功能【XlsxWriter模块】
2019/03/11 Python
Python3.5基础之函数的定义与使用实例详解【参数、作用域、递归、重载等】
2019/04/26 Python
对python中url参数编码与解码的实例详解
2019/07/25 Python
keras tensorflow 实现在python下多进程运行
2020/02/06 Python
python实现替换word中的关键文字(使用通配符)
2020/02/13 Python
解决numpy矩阵相减出现的负值自动转正值的问题
2020/06/03 Python
flask项目集成swagger的方法
2020/12/09 Python
工程力学硕士生的自我评价范文
2013/11/16 职场文书
电气自动化求职信
2014/06/24 职场文书
工作失误检讨书(3篇)
2014/10/11 职场文书
财政局党的群众路线教育实践活动剖析材料
2014/10/13 职场文书
组织生活会发言材料
2014/12/15 职场文书
经典导游欢迎词
2015/01/26 职场文书
公司地址变更通知
2015/04/25 职场文书
2015年幼儿园德育工作总结
2015/05/25 职场文书
Python实现学生管理系统(面向对象版)
2021/06/24 Python
webpack介绍使用配置教程详解webpack介绍和使用
2022/06/25 Javascript