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的鼠标拖动效果代码
May 30 Javascript
JS数组去重与取重的示例代码
Jan 24 Javascript
JQuery插件Marquee.js实现无缝滚动效果
Apr 26 Javascript
BootStrap入门教程(二)之固定的内置样式
Sep 19 Javascript
AngularJS的ng-repeat指令与scope继承关系实例详解
Jan 21 Javascript
js中toString()和String()区别详解
Mar 23 Javascript
JavaScript实现无穷滚动加载数据
May 06 Javascript
vue-router实现tab标签页(单页面)详解
Oct 17 Javascript
Layui带搜索的下拉框的使用以及动态数据绑定方法
Sep 28 Javascript
详解如何在Vue项目中发送jsonp请求
Oct 25 Javascript
vue中使用rem布局代码详解
Oct 30 Javascript
vue实现短信验证码输入框
Apr 17 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
laravel使用Faker数据填充的实现方法
2019/04/12 PHP
PHP 文件写入和读取操作实例详解【必看篇】
2019/11/04 PHP
Laravel框架Blade模板简介及模板继承用法分析
2019/12/03 PHP
jQuery+css实现的切换图片功能代码
2016/01/27 Javascript
浅谈javascript中关于日期和时间的基础知识
2016/07/13 Javascript
Bootstrap CSS布局之按钮
2016/12/17 Javascript
Three.js获取鼠标点击的三维坐标示例代码
2017/03/24 Javascript
浅谈Vue.use的使用
2018/08/29 Javascript
vue实现固定位置显示功能
2019/05/30 Javascript
HTML+JavaScript实现扫雷小游戏
2019/09/30 Javascript
解决angular 使用原生拖拽页面卡顿及表单控件输入延迟问题
2020/04/21 Javascript
ant design vue 表格table 默认勾选几项的操作
2020/10/31 Javascript
python读写ini文件示例(python读写文件)
2014/03/25 Python
python多线程用法实例详解
2015/01/15 Python
python 提取tuple类型值中json格式的key值方法
2018/12/31 Python
python字符串查找函数的用法详解
2019/07/08 Python
python实现邮件发送功能
2019/08/10 Python
python3.5 cv2 获取视频特定帧生成jpg图片
2019/08/28 Python
使用CSS3实现input多选框自定义样式的方法示例
2019/07/19 HTML / CSS
秘鲁购物网站:Linio秘鲁
2017/04/07 全球购物
宝信软件JAVA工程师面试经历
2012/08/19 面试题
linux面试题参考答案(6)
2016/06/23 面试题
动物学专业毕业生求职信
2013/10/11 职场文书
董事长职责范文
2013/11/08 职场文书
教学个人的自我评价分享
2014/02/16 职场文书
纠风工作实施方案
2014/03/15 职场文书
2014年高中生自我评价范文
2014/09/26 职场文书
学校群众路线专项整治方案
2014/10/31 职场文书
小学三八妇女节活动总结
2015/02/06 职场文书
2016年秋季开学典礼新闻稿
2015/11/25 职场文书
介绍信应该怎么开?
2019/04/03 职场文书
情侣餐厅的创业计划书范本!
2019/07/26 职场文书
话题作文之关于呼唤
2019/11/29 职场文书
浅析Django接口版本控制
2021/06/26 Python
使用 Apache 反向代理的设置技巧
2022/01/18 Servers
SQL Server携程核心系统无感迁移到MySQL实战
2022/06/01 SQL Server