anyOf案例

wen java案例 2

本文目录导读:

anyOf案例

  1. 场景 1:JSON Schema 中的 anyOf(API 数据校验)
  2. 场景 2:TypeScript 中的等效实现( 联合类型 + 类型守卫)
  3. 场景 3:使用 Zod(类库优雅实现)
  4. 易混淆点对比

在编程中(尤其是JSON SchemaTypeScriptZodOpenAPI等场景),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 组合(类型层面更安全)。

抱歉,评论功能暂时关闭!