TypeScript中实现互斥类型(XOR)的完整指南
本文详解如何在TypeScript中定义互斥类型,确保对象属性中仅有一个存在。通过结合Never类型、Exclude工具类型及泛型,构建通用的XOR类型解决方案,并深入解析其编译期报错原理与测试用例。
前言
假设存在一个对象,包含name和title两个属性。在赋值时,这两个属性必须互斥,即出现name时title不可出现,反之亦然。
面对这一需求,如何利用TypeScript进行类型定义?本文将介绍一种互斥类型的实现方案,供开发者参考。
前置知识
在深入实现之前,需掌握以下基础概念。
对象中多属性同类型的定义
若对象包含a、b、c、d、e五个可选属性,且类型均为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类型是所有类型的子类型,无法再被细分。这意味着除了never本身,没有任何其他类型可以赋值给它。
通过示例说明:
- 定义变量amazing并赋予never类型。
- 尝试赋予number、boolean、string、object或array等不同类型的值,编译均会失败,因为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,可使用TypeScript内置的Exclude工具类型。该函数接收两个参数:
- UnionType:联合类型
- ExcludedMembers:需要剔除的属性集合
具体用法如下。
type P = Exclude<"a" | "b" | "c" | "d", "b" | "c"> // "a" | "d"将对象所有属性转为联合类型
若需将包含name和title两个可选属性的对象转为联合类型name | title,可使用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官方文档中关于泛型的章节。
测试用例
将文章开头的场景代入上述代码,验证其有效性。
// 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,赋予任何值均会导致类型不匹配。

用例拆解
部分开发者可能对测试用例感到困惑,虽前置知识已讲解各部分,但组合后可能难以理解。以下将详细拆解代码逻辑。
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: "" }; // 报错建议开发者在编辑器中亲自编写测试,若仍不理解,可先收藏本文,日后深入学习。
写在最后
本文到此结束。
至此,关于TypeScript中互斥类型实现的文章介绍完毕。如需了解更多TypeScript互斥类型相关内容,请查阅本站过往文章或继续阅读下文。感谢大家的支持!
JS动态构建复杂嵌套JSON对象的方法
2026-11-05 15:41:52
JS负数取余为什么是负数?详解余数与模运算区别
2026-11-05 13:34:41
JS 解压出现乱码或报错怎么解决?
2026-11-05 12:32:38
JS 中 startsWith、indexOf 与正则判断字符串前缀的对比
2026-11-04 22:19:43
JS解析Excel后怎么将数据转换为JSON格式
2026-11-04 20:58:28








