实现TypeScript中的互斥类型

简介: 实现TypeScript中的互斥类型

前言


有这样一个对象,它有两个属性:nametitle,在赋值的时候这两个属性只有一个能出现,例如:name出现的时候title就不能出现,title出现的时候name就不能出现。

此时,你会怎么用TypeScript来定义这个类型?本文将带大家实现一个互斥类型来解决这个问题,欢迎各位感兴趣的开发者阅读本文。


前置知识


在实现之前,我们需要先来了解几个基础的知识。


对象中多属性同类型的定义


有一个对象它包含5个可选属性abcde,他们的类型都为string,大多数人的定义方式应该如下所示:


type obj = {
  a?:string;
  b?:string;
  c?:string;
  d?:string;
  e?:string;
}


那么,有没有更好的方式呢😼,答案是有的,请看我的表演:


type obj = { [P in "a" | "b" | "c" | "d" | "e"]?: string };


never类型


在TypeScript中它有一个特殊的类型never,它是所有类型的子类型,无法再进行细分,也就意味着除了其本身没有类型可以再分配给它。


我们举个例子来解释下上述话语,如下所示:


  • 我们定义了一个变量amazing,给其赋予了never类型。
  • 我们分别给它赋了不同类型的值,全部编译失败,因为它无法再进行细分了。


let amazing: never;
amazing = 12;// 报错:amazing是never类型不能分配给number类型
amazing = true;// 报错:amazing是never类型不能分配给boolean类型
amazing = "真神奇";// 报错:amazing是never类型不能分配给string类型
amazing = {};// 报错:amazing是never类型不能分配给{}类型
amazing = [];// 报错:amazing是never类型不能分配给[]类型


剔除联合类型中的属性


有一组联合类型"a" | "b" | "c" | "d",我们想剔除属性b和c,在TS中提供了一个名为Exclude的函数,它可以用来做这件事,接受两个参数:


  • UnionType 联合类型
  • ExcludedMembers 需要进行剔除的属性


使用方法如下所示:


type P = Exclude<"a" | "b" | "c" | "d", "b" | "c"> // "a" | "d"


将对象中的所有属性转为联合类型


有一个对象它包含2个可选属性nametitle,我们想把它转为联合类型name | title ,在TS中提供了一个名为keyof的函数,他可以用来处理这个问题,使用方法如下所示:


type A =  { [P in "name" | "title"]?: string };
type UnionType = keyof A; // "name" | "title"


实现互斥类型


有了前置知识作为铺垫,接下来我们就可以将其利用起来,定义一个互斥类型出来,解决文章开头所讲述的问题。


接下来,我们来梳理下实现思路:


  • 实现一个排除类型,用于从A对象类型中剔除B对象类型中的属性,并将排除后的属性类型设为never,得到一个新对象类型。
  • 基于排除类型实现互斥类型,将A、B对象类型代入排除类型中,彼此将其排除,用或运算符将二者结果连接。


聪明的开发者可能已经猜到原理了,没错,就是部分属性设为never。🤓


实现代码


接下来,我们来看下代码的实现,如下所示:


// 定义排除类型:将U从T中剔除, keyof 会取出T与U的所有键, 限定P的取值范围为T中的所有键, 并将其类型设为never
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
// 定义互斥类型,T或U只有一个能出现(互相剔除时,被剔除方必须存在)
type XOR<T, U> = (Without<T, U> & U) | (Without<U, T> & T);


注意:为了类型的可复用性,我们使用了泛型,对此不熟悉的开发者请移步:TypeScript中文网——泛型[1]


测试用例


我们将文章开头所说的问题代入上述实现代码中,看一下它能否将其解决😌,如下所示:


// A类型
type A = {
  name: string;
};
// B类型
type B = {
  title: string;
};
// A和B两种类型只有一个能出现
type AOrB = XOR<A, B>;
// 传值测试
const AOrB1: AOrB = { name: "姓名" }; // 编译通过
const AOrB2: AOrB = { title: "标题" }; // 编译通过
const AOrB3: AOrB = { title: "标题", name: "姓名" }; // 报错: Type '{ title: string; name: string; }' is not assignable to type 'AOrB'.
const AOrB4: AOrB = { name: "姓名", otherKey: "" }; // 报错:Type '{ name: string; otherKey: string; }' is not assignable to type 'AOrB'.


当两个属性同时出现时,编辑器直接就抛出了类型错误(我们把排除后的所有属性的类型设为了never,因此当你给其赋任何值时它都会报类型错误),如下图所示:


640.png

                         image-20220409221841105


用例拆解


有一部分开发者可能对上述测试用例比较懵,把它们拆开都认识,因为前置知识里都讲了,但是写到一起就不认识了😹,没关系,那我就把它们都拆解出来吧,代码如下所示:


type AOB = ({ name?: never } & {
      title: string;
    }) | ({ title?: never } & {
      name: string;
    });
// 传值测试
const a: AOB = { name: "姓名" }; // 编译通过
const b: AOB = { title: "标题" }; // 编译通过
const c: AOB = { title: "标题", name: "姓名" }; // 报错
const d: AOB = { title: "标题", otherKey: "" }; // 报错


看到这里,可能还有一部分开发者没有理解,那就动起手来在编辑器里敲一敲,如果还没理解的话,就先把这篇文章收藏,日后有时间了,拿出来学一学。


写在最后


至此,文章就分享完毕了。·


我是神奇的程序员,一位前端开发工程师。


如果你对我感兴趣,请移步我的个人网站[2],进一步了解。


  • 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注😊
  • 文中链接可从文末参考资料中获取
相关文章
|
2月前
|
JavaScript 前端开发 安全
Apollo与TypeScript:强大类型检查在前端开发中的应用
Apollo与TypeScript:强大类型检查在前端开发中的应用
|
3月前
|
JavaScript 前端开发
TypeScript 联合类型
TypeScript 联合类型
35 0
|
3月前
|
存储 JavaScript 前端开发
2020你应该知道的TypeScript学习路线【数组类型】
2020你应该知道的TypeScript学习路线【数组类型】
33 0
|
5天前
|
JavaScript 前端开发 编译器
TypeScript中的高级类型:联合类型、交叉类型与条件类型深入解析
【4月更文挑战第23天】探索TypeScript的高级类型。这些特性增强类型系统的灵活性,提升代码质量和维护性。
|
1月前
|
JavaScript 安全
TypeScript 中的高级类型转换技术:映射类型、条件类型和类型推断
TypeScript 中的高级类型转换技术:映射类型、条件类型和类型推断
|
1月前
react+typescript给state和props定义指定类型
react+typescript给state和props定义指定类型
16 1
|
2月前
|
JavaScript 安全 索引
TypeScript泛型和类型体操
泛型和类型体操(Type Gymnastics)是 TypeScript 中高级类型系统的重要组成部分。它们提供了强大的工具和技巧,用于处理复杂的类型操作和转换。
|
2月前
|
JavaScript 安全 容器
Vue3 + setup + TypeScript: 构建现代、类型安全的Vue应用的关键技巧总结
当使用 setup 的时候,组件直接引入就可以了,不需要再自己手动注册
|
3月前
|
JavaScript 前端开发 IDE
【TypeScript】带类型语法的JavaScript
【1月更文挑战第26天】【TypeScript】带类型语法的JavaScript
|
3月前
|
JavaScript
小结:近五十个常用 TypeScript类型工具 的声明、描述、用法示例
小结:近五十个常用 TypeScript类型工具 的声明、描述、用法示例
128 0