本文目录导读:

在编程中(尤其是JSON Schema、TypeScript、Zod、OpenAPI等场景),anyOf 是一个组合类型(Composition)关键字,用于定义“满足多个条件中的任意一个即可”。
下面我通过最典型的 JSON Schema 和 TypeScript 来展示 anyOf 的实战案例和底层逻辑。
场景 1:JSON Schema 中的 anyOf(API 数据校验)
假设你正在设计一个支付系统 API,用户提交的支付方式必须是 “信用卡” 或 “支付宝” 之一,但两者的数据结构完全不同。
基础版(宽松校验)
{
"$schema": "http://json-schema.org/draft-07/schema#",: "Payment Method",
"type": "object",
"anyOf": [
{
"properties": {
"cardNumber": { "type": "string", "pattern": "^[0-9]{16}$" },
"cvv": { "type": "string", "pattern": "^[0-9]{3}$" }
},
"required": ["cardNumber", "cvv"],
"additionalProperties": false // 禁止额外属性
},
{
"properties": {
"alipayAccount": { "type": "string", "format": "email" },
"oauthToken": { "type": "string" }
},
"required": ["alipayAccount", "oauthToken"],
"additionalProperties": false
}
]
}
测试用例
- ✅ 合法:
{ "cardNumber": "1234567812345678", "cvv": "123" } - ✅ 合法:
{ "alipayAccount": "user@xx.com", "oauthToken": "abc123" } - ❌ 非法:
{ "cardNumber": "1234", "cvv": "12" }(两个分支都失败) - ❌ 非法:
{ "cardNumber": "1234567812345678", "cvv": "123", "alipayAccount": "x@y.com" }(虽然第一个分支成功了,但第二个分支因缺少oauthToken失败。注意:anyOf 只要有一个成功就通过校验,这里第一个分支成功,所以整体是合法的,除非你额外加了maxProperties限制)。
核心要点:
anyOf只要至少一个分支通过校验,整体即通过,它和oneOf的区别是:oneOf要求恰好一个(用于区分互斥场景)。
场景 2:TypeScript 中的等效实现( 联合类型 + 类型守卫)
TypeScript 原生使用 运算符,但在运行时校验(比如接收外部数据)时,我们需要自定义类型守卫来模拟 anyOf 逻辑。
定义类型(相当于 JSON Schema 的两个分支)
type CreditCard = {
cardNumber: string;
cvv: string;
};
type Alipay = {
alipayAccount: string;
oauthToken: string;
};
// 联合类型 = anyOf
type PaymentMethod = CreditCard | Alipay;
运行时校验(纯手写实现 anyOf)
function isCreditCard(obj: any): obj is CreditCard {
return (
typeof obj.cardNumber === 'string' &&
/^[0-9]{16}$/.test(obj.cardNumber) &&
typeof obj.cvv === 'string' &&
/^[0-9]{3}$/.test(obj.cvv)
);
}
function isAlipay(obj: any): obj is Alipay {
return (
typeof obj.alipayAccount === 'string' &&
obj.alipayAccount.includes('@') &&
typeof obj.oauthToken === 'string'
);
}
// anyOf 的实现:只要有一个为真即为真
function isPaymentMethod(obj: any): obj is PaymentMethod {
return isCreditCard(obj) || isAlipay(obj);
}
实际使用
const rawData = JSON.parse('{"cardNumber":"1234567812345678","cvv":"123"}');
if (isPaymentMethod(rawData)) {
// TypeScript 自动收窄类型为 CreditCard | Alipay
if ('cardNumber' in rawData) {
console.log('这是信用卡', rawData.cvv);
} else {
console.log('这是支付宝', rawData.oauthToken);
}
} else {
console.error('不满足任何支付方式');
}
场景 3:使用 Zod(类库优雅实现)
Zod 内置了 .or() 方法,直接映射 JSON Schema 的 anyOf。
import { z } from 'zod';
// 定义两个 schema
const CardSchema = z.object({
cardNumber: z.string().regex(/^[0-9]{16}$/),
cvv: z.string().regex(/^[0-9]{3}$/),
});
const AlipaySchema = z.object({
alipayAccount: z.string().email(),
oauthToken: z.string(),
});
// anyOf 实现
const PaymentSchema = CardSchema.or(AlipaySchema); // 或者 z.union([CardSchema, AlipaySchema])
// 使用
const result = PaymentSchema.safeParse({ cardNumber: '...', ... });
if (result.success) {
console.log('校验通过,这是其中的一种支付方式');
} else {
console.log('两种都不是');
}
易混淆点对比
| 关键字/运算符 | 要求 | 典型使用场景 |
|---|---|---|
anyOf (或 ) |
至少满足一个(允许多个同时满足) | 只要数据属于任一合法形态即可,不关心冲突 |
oneOf |
恰好满足一个(多个满足则报错) | 多态数据,必须明确区分是哪一种类型(如动物分类) |
allOf (或 &) |
必须全部满足 | 组合复用多个基础 schema 的公共字段 |
anyOf 的核心逻辑就是“或”运算(OR)。 在实际开发中,它很好地解决了异步数据格式混合(比如一个接口根据 type 字段返回不同的结构)的校验问题。
你可以根据自己的技术栈选择:
- 原生 JS: 手写
if (isA || isB)。 - TS / Zod: 使用
union组合(类型层面更安全)。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播