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 相关文章推荐
浏览器窗口大小变化时使用resize事件对框架不起作用的解决方法
May 11 Javascript
简单介绍JavaScript中字符串创建的基本方法
Jul 07 Javascript
HTML5之WebSocket入门3 -通信模型socket.io
Aug 21 Javascript
基于javascript实现漂亮的页面过渡动画效果附源码下载
Oct 26 Javascript
Bootstrap~多级导航(级联导航)的实现效果【附代码】
Mar 08 Javascript
JavaScript弹出对话框的三种方式
Mar 23 Javascript
使用jQuery Mobile框架开发移动端Web App的入门教程
May 17 Javascript
利用three.js画一个3D立体的正方体示例代码
Nov 19 Javascript
基于javascript的拖拽类封装详解
Apr 19 Javascript
layui动态渲染生成select的option值方法
Sep 23 Javascript
JS实现打字游戏
Dec 17 Javascript
vue实现下拉菜单树
Oct 22 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
《五等分的花嫁》漫画完结!2020年10月第2期TV动画制作组换血!
2020/03/06 日漫
php制作圆形用户头像的实例_自定义封装类源代码
2017/09/18 PHP
Javascript学习笔记6 prototype的提出
2010/01/11 Javascript
兼容IE和FF的js脚本代码小结(比较常用)
2010/12/06 Javascript
jquery中使用ajax获取远程页面信息
2011/11/13 Javascript
JS常见问题整理(持续更新)
2013/08/06 Javascript
js设置组合快捷键/tabindex功能的方法
2013/11/21 Javascript
js限制checkbox选中个数以限制六个为例
2014/07/15 Javascript
javascript的replace方法结合正则使用实例总结
2016/06/16 Javascript
Javascript在IE和Firefox浏览器常见兼容性问题总结
2016/08/03 Javascript
基于HTML+CSS+JS实现增加删除修改tab导航特效代码
2016/08/05 Javascript
在js中做数字字符串补0(js补零)
2017/03/25 Javascript
如何在vue里添加好看的lottie动画
2018/08/02 Javascript
详解在Javascript中进行面向切面编程
2019/04/28 Javascript
vue2.0 实现富文本编辑器功能
2019/05/26 Javascript
vuex实现像调用模板方法一样调用Mutations方法
2019/11/06 Javascript
基于JS判断对象是否是数组
2020/01/10 Javascript
微信小程序 flexbox layout快速实现基本布局的解决方案
2020/03/24 Javascript
Vue单文件组件开发实现过程详解
2020/07/30 Javascript
JavaScript实现多球运动效果
2020/09/07 Javascript
vue-cli 3如何使用vue-bootstrap-datetimepicker日期插件
2021/02/20 Vue.js
Python遍历指定文件及文件夹的方法
2015/05/09 Python
Python实现在matplotlib中两个坐标轴之间画一条直线光标的方法
2015/05/20 Python
使用pdb模块调试Python程序实例
2015/06/02 Python
详解Python用户登录接口的方法
2019/04/17 Python
深入了解Django中间件及其方法
2019/07/26 Python
Pycharm中安装Pygal并使用Pygal模拟掷骰子(推荐)
2020/04/08 Python
CSS3基础(RGBa、text-shadow、box-shadow、border-radius)
2012/11/13 HTML / CSS
CSS3 制作旋转的大风车(充满童年回忆)
2013/01/30 HTML / CSS
采购部部门职责
2013/12/15 职场文书
大学毕业生自荐书怎么写?
2014/01/06 职场文书
质量月活动总结
2014/08/26 职场文书
SQL Server代理:理解SQL代理错误日志处理方法
2021/06/30 SQL Server
一文了解JavaScript用Element Traversal新属性遍历子元素
2021/11/27 Javascript
为Centos安装指定版本的Docker
2022/04/01 Servers
Python Matplotlib绘制两个Y轴图像
2022/04/13 Python