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的资料请关注我们其它相关文章!

(0)

相关推荐

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

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

  • 深入了解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

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

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

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

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

  • TypeScript 内置高级类型编程示例

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

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

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

  • 直观详细的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实用技巧 Nominal Typing名义类型详解

    目录 Nominal Typing(名义类型) 概念解析 拓展应用 在Vue中的应用 Nominal Typing(名义类型) 概念解析 意思是给一个类型附加上一个“名义”,从而防止结构类型在某些情况下由于类型结构相似而被错用.假设有如下代码: interface Vector2D { x: number, y: number }; interface Vector3D { x: number, y: number, z: number }; function calc(vector: Vect

  • TypeScript声明文件的语法与场景详解

    目录 简介 语法 内容 模块化 模块语法 三斜线指令 reference amd-module 场景 1. 在内部项目中给内部项目写声明文件 2. 给第三方包写声明文件 全局变量的第三方库 修改全局变量的模块的第三方库的声明 修改window ESM和CommonJS UMD 模块插件 总结 简介 声明文件是以.d.ts为后缀的文件,开发者在声明文件中编写类型声明,TypeScript根据声明文件的内容进行类型检查.(注意同目录下最好不要有同名的.ts文件和.d.ts,例如lib.ts和lib.

  • TypeScript 背后的结构化类型系统原理详解

    目录 前言 什么是结构化类型系统? 什么是标称类型系统? 结构化类型系统等价于鸭子类型系统吗? 如何在 TypeScript 中模拟标称类型系统? 交叉类型实现 类实现 总结 前言 你能说清楚类型.类型系统.类型检查这三个的区别吗?在理解TypeScript的结构化类型系统之前,我们首先要搞清楚这三个概念和它们之间的关系 类型:即对变量的访问限制与赋值限制.如 TypeScript 中的原始类型.对象类型.函数类型和字面量类型等类型,当一个变量类型确定后,你不能访问这个类型中不存在的属性或方法,

  • typescript nodejs 依赖注入实现方法代码详解

    依赖注入通常也是我们所说的ioc模式,今天分享的是用typescript语言实现的ioc模式,这边用到的主要组件是 reflect-metadata 这个组件可以获取或者设置元数据信息,它的作用是拿到原数据后进行对象创建类似C#中的反射,先看第一段代码: import "reflect-metadata"; /** * 对象管理器 */ const _partialContainer = new Map<string, any>(); const PARAMTYPES =

  • 使用Typescript开发微信小程序的步骤详解

    Typescript的优势咱不需要赘述太多,有兴趣可以参考(https://www.typescriptlang.org/).今天给大家分享一下如何在微信小程序(或者其他同类小程序)开发中使用Typescript. 这个分两种情况,最简单的做法就是在创建项目时,选择Typescript这个选项,如下图所示.但要注意,这个选项只有在选择"Use no cloud service"才有,而另外一种Mini Program Cloud Base则不支持.这个可能是开发工具还没有跟上吧,希望以

  • 非常实用的MySQL函数全面总结详解示例分析教程

    目录 1.MySQL中关于函数的说明 2.单行函数分类 3.字符函数 4.数学函数 5.日期时间函数 6.其它常用系统函数 7.流程控制函数 8.聚合函数 1)聚合函数的功能和分类: 2)聚合函数的简单使用 3)五个聚合函数中传入的参数,所支持的数据类型有哪些? 4)聚合函数和group by的使用"最重要": 1.MySQL中关于函数的说明 "概念":类似java.python中的方法,将一组逻辑语句封装在方法体中,对外暴露方法名: "好处":

  • C语言大厂面试技巧及strcpy()函数示例详解

    目录 1.什么是优秀的代码? 2.常见的coding技巧有哪些? 3.以模拟实现strcpy为例 (1)了解strcpy()函数 (2)正片开始(危) 1.第一阶段(面试官:最多5分) 2.第二阶段(面试官:最多7分) 3.第三阶段的代码:(面试官:最多8分) 4.第四阶段(面试官:完美代码!10分!) 1.什么是优秀的代码? 1. 代码运行正常 2. bug很少 3. 效率高 4. 可读性高 5. 可维护性高 6. 注释清晰 7. 文档齐全 2.常见的coding技巧有哪些? 1. 使用ass

  • Python必备技巧之字符数据操作详解

    目录 字符串操作 字符串 + 运算符 字符串 * 运算符 字符串 in 运算符 内置字符串函数 字符串索引 字符串切片 字符串切片中的步幅 将变量插入字符串 修改字符串 内置字符串方法 bytes对象 定义文字bytes对象 bytes使用内置bytes()函数定义对象 bytes对象操作,操作参考字符串. bytearray对象,Python 支持的另一种二进制序列类型 字符串操作 字符串 + 运算符 +运算符用于连接字符串,返回一个由连接在一起的操作数组成的字符串. >>> s =

  • Python必备技巧之函数的使用详解

    目录 1.如何用函数 2.默认参数陷阱 2.1针对可变数据类型,不可变不受影响 3.名称空间和作用域 4.闭包函数 5.函数的参数 5.1定义阶段 5.2调用阶段 6.装饰器:闭包函数的应用 6.1装饰器的实现必须遵循两大原则 6.2装饰器语法糖 6.3无参装饰器 6.4有参装饰器 7.题目 1.如何用函数 先定义后调用,定义阶段只检测语法,不执行代码 调用阶段,开始执行代码 函数都有返回值 定义时无参,调用时也是无参 定义时有参,调用时也必须有参 2.默认参数陷阱 2.1针对可变数据类型,不可

  • TypeScript新语法之infer extends示例详解

    目录 正文 模式匹配 提取枚举的值的类型 总结 正文 我们知道,TypeScript 支持 infer 来提取类型的一部分,通过模式匹配的方式. 模式匹配 比如元组类型提取最后一个元素的类型: type Last<Arr extends unknown[]> = Arr extends [...infer rest,infer Ele] ? Ele : never; 比如函数提取返回值类型: type GetReturnType<Func extends Function> = F

随机推荐