schema-dsl 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/CHANGELOG.md +130 -113
  2. package/LICENSE +21 -21
  3. package/README.md +628 -628
  4. package/dist/{DslBuilder-DkLaOo9Q.d.ts → DslBuilder-BIgQOAXp.d.ts} +2 -0
  5. package/dist/{DslBuilder-DQDN0ZxZ.d.cts → DslBuilder-CjHTucNQ.d.cts} +2 -0
  6. package/dist/{Validator-hFWKGxir.d.ts → Validator-CllRdrY0.d.ts} +1 -1
  7. package/dist/{Validator-C7GsVQOH.d.cts → Validator-D6okG9tr.d.cts} +1 -1
  8. package/dist/index.cjs +75 -29
  9. package/dist/index.d.cts +10 -4
  10. package/dist/index.d.ts +10 -4
  11. package/dist/index.js +75 -29
  12. package/dist/plugins/custom-format.cjs +33 -17
  13. package/dist/plugins/custom-format.d.cts +1 -1
  14. package/dist/plugins/custom-format.d.ts +1 -1
  15. package/dist/plugins/custom-format.js +33 -17
  16. package/dist/plugins/custom-type-example.cjs +33 -17
  17. package/dist/plugins/custom-type-example.d.cts +1 -1
  18. package/dist/plugins/custom-type-example.d.ts +1 -1
  19. package/dist/plugins/custom-type-example.js +33 -17
  20. package/dist/plugins/custom-validator.cjs +0 -2
  21. package/dist/plugins/custom-validator.d.cts +1 -1
  22. package/dist/plugins/custom-validator.d.ts +1 -1
  23. package/dist/plugins/custom-validator.js +0 -2
  24. package/docs/FEATURE-INDEX.md +553 -553
  25. package/docs/add-custom-locale.md +496 -496
  26. package/docs/add-keyword.md +24 -24
  27. package/docs/api-reference.md +1047 -1047
  28. package/docs/api.md +13 -13
  29. package/docs/best-practices-project-structure.md +417 -417
  30. package/docs/best-practices.md +712 -712
  31. package/docs/cache-manager.md +344 -344
  32. package/docs/compile.md +45 -45
  33. package/docs/conditional-api.md +1307 -1307
  34. package/docs/custom-extensions-guide.md +339 -339
  35. package/docs/design-philosophy.md +606 -606
  36. package/docs/doc-index.md +324 -324
  37. package/docs/dsl-syntax.md +714 -714
  38. package/docs/dynamic-locale.md +608 -608
  39. package/docs/enum.md +482 -482
  40. package/docs/error-handling.md +1975 -1975
  41. package/docs/export-guide.md +501 -501
  42. package/docs/export-limitations.md +567 -567
  43. package/docs/faq.md +596 -596
  44. package/docs/frontend-i18n-guide.md +307 -307
  45. package/docs/i18n-user-guide.md +487 -487
  46. package/docs/i18n.md +476 -476
  47. package/docs/index.md +48 -48
  48. package/docs/json-schema-basics.md +40 -40
  49. package/docs/label-vs-description.md +271 -271
  50. package/docs/markdown-exporter.md +406 -406
  51. package/docs/mongodb-exporter.md +302 -302
  52. package/docs/multi-language.md +26 -26
  53. package/docs/multi-type-support.md +322 -322
  54. package/docs/mysql-exporter.md +280 -280
  55. package/docs/number-operators.md +449 -449
  56. package/docs/optional-marker-guide.md +326 -326
  57. package/docs/performance-guide.md +49 -49
  58. package/docs/plugin-system.md +381 -381
  59. package/docs/plugin-type-registration.md +34 -34
  60. package/docs/postgresql-exporter.md +311 -311
  61. package/docs/public/favicon.svg +4 -4
  62. package/docs/quick-start.md +435 -435
  63. package/docs/runtime-locale-support.md +532 -532
  64. package/docs/schema-helper.md +345 -345
  65. package/docs/schema-utils-advanced-issues.md +23 -23
  66. package/docs/schema-utils-best-practices.md +20 -20
  67. package/docs/schema-utils-chaining.md +150 -150
  68. package/docs/schema-utils.md +524 -524
  69. package/docs/security-checklist.md +20 -20
  70. package/docs/string-extensions.md +488 -488
  71. package/docs/troubleshooting.md +486 -486
  72. package/docs/type-converter.md +310 -310
  73. package/docs/type-reference.md +242 -242
  74. package/docs/typescript-guide.md +584 -584
  75. package/docs/union-type-guide.md +157 -157
  76. package/docs/union-types.md +284 -284
  77. package/docs/validate-async.md +491 -491
  78. package/docs/validate-batch.md +49 -49
  79. package/docs/validate-dsl-object-support.md +578 -578
  80. package/docs/validate.md +506 -506
  81. package/docs/validation-guide.md +502 -502
  82. package/docs/validator.md +39 -39
  83. package/package.json +131 -131
  84. package/plugins/custom-format.cjs +8 -8
  85. package/plugins/custom-type-example.cjs +8 -8
  86. package/plugins/custom-validator.cjs +8 -8
  87. package/src/adapters/DslAdapter.ts +111 -111
  88. package/src/adapters/index.ts +1 -1
  89. package/src/config/constants.ts +83 -83
  90. package/src/config/index.ts +2 -2
  91. package/src/config/patterns.ts +77 -77
  92. package/src/core/CacheManager.ts +169 -159
  93. package/src/core/ConditionalBuilder.ts +382 -382
  94. package/src/core/ConditionalRuntime.ts +27 -27
  95. package/src/core/ConditionalValidator.ts +254 -254
  96. package/src/core/DslBuilder.ts +687 -677
  97. package/src/core/ErrorCodes.ts +38 -38
  98. package/src/core/ErrorFormatter.ts +271 -271
  99. package/src/core/JSONSchemaCore.ts +65 -65
  100. package/src/core/Locale.ts +187 -187
  101. package/src/core/MessageTemplate.ts +42 -42
  102. package/src/core/ObjectDslBuilder.ts +64 -64
  103. package/src/core/PluginManager.ts +326 -326
  104. package/src/core/StringExtensions.ts +140 -140
  105. package/src/core/TemplateEngine.ts +44 -44
  106. package/src/core/Validator.ts +448 -448
  107. package/src/errors/I18nError.ts +159 -159
  108. package/src/errors/ValidationError.ts +105 -105
  109. package/src/exporters/BaseExporter.ts +60 -60
  110. package/src/exporters/MarkdownExporter.ts +305 -305
  111. package/src/exporters/MongoDBExporter.ts +126 -126
  112. package/src/exporters/MySQLExporter.ts +156 -155
  113. package/src/exporters/PostgreSQLExporter.ts +222 -222
  114. package/src/exporters/index.ts +18 -18
  115. package/src/index.ts +651 -633
  116. package/src/locales/en-US.ts +160 -160
  117. package/src/locales/es-ES.ts +160 -160
  118. package/src/locales/fr-FR.ts +160 -160
  119. package/src/locales/index.ts +103 -103
  120. package/src/locales/ja-JP.ts +160 -160
  121. package/src/locales/types.ts +156 -156
  122. package/src/locales/zh-CN.ts +160 -160
  123. package/src/parser/ConstraintParser.ts +101 -101
  124. package/src/parser/DslParser.ts +470 -470
  125. package/src/parser/SchemaCompiler.ts +66 -66
  126. package/src/parser/TypeRegistry.ts +250 -250
  127. package/src/parser/index.ts +6 -6
  128. package/src/plugins/custom-format.ts +124 -126
  129. package/src/plugins/custom-type-example.ts +106 -108
  130. package/src/plugins/custom-validator.ts +138 -140
  131. package/src/types/conditional.ts +28 -28
  132. package/src/types/config.ts +59 -59
  133. package/src/types/dsl.ts +131 -131
  134. package/src/types/error.ts +60 -60
  135. package/src/types/index.ts +17 -17
  136. package/src/types/infer.ts +127 -127
  137. package/src/types/plugin.ts +58 -58
  138. package/src/types/safe-regex.d.ts +9 -9
  139. package/src/types/schema.ts +66 -66
  140. package/src/types/validate.ts +71 -71
  141. package/src/utils/SchemaHelper.ts +196 -196
  142. package/src/utils/SchemaUtils.ts +365 -346
  143. package/src/utils/TypeConverter.ts +215 -215
  144. package/src/utils/index.ts +10 -10
  145. package/src/validators/CustomKeywords.ts +477 -477
@@ -1,1307 +1,1307 @@
1
- # 链式条件 API - ConditionalBuilder
2
-
3
- > **版本**: schema-dsl v2.0.0-beta.2
4
- > **更新日期**: 2026-05-08
5
- > **状态**: ✅ 稳定
6
-
7
- ---
8
-
9
- ## 📋 目录
10
-
11
- - [概述](#概述)
12
- - [🆕 v1.1.1 新功能](#-v110-新功能)
13
- - [快速开始](#快速开始)
14
- - [API 参考](#api-参考)
15
- - [使用场景](#使用场景)
16
- - [最佳实践](#最佳实践)
17
- - [常见问题](#常见问题)
18
-
19
- ---
20
-
21
- ## 概述
22
-
23
- `ConditionalBuilder` 提供流畅的链式条件判断 API,类似 JavaScript 的 if-else 语句,用于在验证时根据实际数据动态调整验证规则。
24
-
25
- > 关键语义:当你使用 `.message()` / `.assert()` / `.check()` 这类“失败即返回”的模式时,条件函数应该写成**失败条件**,因为条件返回 `true` 才会被判定为失败。
26
-
27
- ### 核心特性
28
-
29
- - ✅ **链式调用** - 流畅的 API,类似 JavaScript if-else
30
- - ✅ **运行时执行** - 在验证时根据实际数据判断
31
- - ✅ **多条件组合** - 支持 and/or 逻辑组合
32
- - ✅ **🆕 独立消息** - v1.1.1+ 每个 .and()/.or() 可有独立错误消息
33
- - ✅ **else 可选** - 不写 else 就不验证
34
- - ✅ **简化设计** - message 自动抛错,无需 throwError()
35
- - ✅ **完全兼容** - 不影响现有 API
36
-
37
- ---
38
-
39
- ## 🆕 v1.1.1 新功能
40
-
41
- ### 独立消息支持 - `.and()/.or()` 后可调用 `.message()`
42
-
43
- **每个条件都可以有自己的错误消息**
44
-
45
- v1.1.1 开始,支持在 `.and()` 和 `.or()` 后调用 `.message()` 设置独立的错误消息,让错误提示更精确。
46
-
47
- #### 基础用法
48
-
49
- ```javascript
50
- const { dsl } = require('schema-dsl');
51
-
52
- // ✅ v1.1.1+ 新功能:每个条件独立消息
53
- dsl.if(d => !d)
54
- .message('ACCOUNT_NOT_FOUND')
55
- .and(d => d.tradable_credits < amount)
56
- .message('INSUFFICIENT_TRADABLE_CREDITS')
57
- .assert(account);
58
-
59
- // 工作原理:
60
- // - 第一个条件为 true → 返回 'ACCOUNT_NOT_FOUND'
61
- // - 第二个条件为 true → 返回 'INSUFFICIENT_TRADABLE_CREDITS'
62
- // - 所有条件为 false → 验证成功
63
- ```
64
-
65
- #### 多个 .and() 条件
66
-
67
- ```javascript
68
- // 多层验证,每层都有清晰的错误消息
69
- dsl.if(d => !d)
70
- .message('ACCOUNT_NOT_FOUND')
71
- .and(d => d.status !== 'active')
72
- .message('ACCOUNT_INACTIVE')
73
- .and(d => d.tradable_credits < amount)
74
- .message('INSUFFICIENT_TRADABLE_CREDITS')
75
- .assert(account);
76
-
77
- // 依次检查,第一个为 true 的条件返回其消息
78
- ```
79
-
80
- #### .or() 条件独立消息
81
-
82
- ```javascript
83
- // OR 条件也支持独立消息
84
- dsl.if(d => d.age < 18)
85
- .message('未成年用户不能注册')
86
- .or(d => d.isBlocked)
87
- .message('账户已被封禁')
88
- .assert(data);
89
-
90
- // 任一条件为 true 就失败,返回对应消息
91
- ```
92
-
93
- #### 链式检查模式
94
-
95
- v1.1.1 引入了**链式检查模式**,当满足以下条件时自动启用:
96
-
97
- 1. 使用 `.message()` 模式(不是 `.then()`/`.else()`)
98
- 2. root 条件有 `.message()`
99
- 3. 有 `.and()` 条件
100
- 4. 没有 `.or()` 条件
101
-
102
- **链式检查模式特点**:
103
- - 依次检查每个条件
104
- - 第一个为 `true` 的条件失败,返回其消息
105
- - 所有条件为 `false` 时验证通过
106
-
107
- **示例对比**:
108
-
109
- ```javascript
110
- // ✅ 启用链式检查(纯 AND 场景)
111
- dsl.if(d => !d).message('A').and(d => d < 100).message('B')
112
-
113
- // ❌ 不启用(有 .or(),使用传统 AND/OR 逻辑)
114
- dsl.if(d => !d).message('A').and(d => d < 100).or(d => d > 200).message('B')
115
-
116
- // ❌ 不启用(使用 .then()/.else(),不是 message 模式)
117
- dsl.if(d => d.age >= 18).and(d => d.role === 'admin').then('email!')
118
- ```
119
-
120
- #### 向后兼容性
121
-
122
- **100% 向后兼容**,不影响现有代码:
123
-
124
- ```javascript
125
- // ✅ 原有用法继续工作
126
- dsl.if(d => d.age >= 18).and(d => d.role === 'admin').message('不符合条件')
127
-
128
- // ✅ .and() 后不调用 .message() 也可以
129
- dsl.if(d => !d).message('整体错误').and(d => d < 100).assert(50)
130
- // → 使用整体消息 '整体错误'
131
- ```
132
-
133
- #### 实际应用场景
134
-
135
- **场景1:账户验证**
136
- ```javascript
137
- function validateAccount(account, amount) {
138
- dsl.if(d => !d)
139
- .message('ACCOUNT_NOT_FOUND')
140
- .and(d => d.status !== 'active')
141
- .message('ACCOUNT_INACTIVE')
142
- .and(d => d.balance < amount)
143
- .message('INSUFFICIENT_BALANCE')
144
- .assert(account);
145
- }
146
-
147
- // 每个失败点都有清晰的错误消息
148
- ```
149
-
150
- **场景2:用户权限验证**
151
- ```javascript
152
- function validateUserPermission(user) {
153
- dsl.if(d => d.role !== 'admin')
154
- .message('NO_ADMIN_PERMISSION')
155
- .and(d => !d.isVerified)
156
- .message('USER_NOT_VERIFIED')
157
- .and(d => d.isBanned)
158
- .message('USER_BANNED')
159
- .assert(user);
160
- }
161
- ```
162
-
163
- **场景3:订单状态检查**
164
- ```javascript
165
- function validateOrder(order) {
166
- dsl.if(d => d.status !== 'paid')
167
- .message('ORDER_NOT_PAID')
168
- .and(d => !d.payment)
169
- .message('PAYMENT_INFO_MISSING')
170
- .and(d => !d.shippingAddress)
171
- .message('SHIPPING_ADDRESS_MISSING')
172
- .assert(order);
173
- }
174
- ```
175
-
176
- ---
177
-
178
- ## 与现有方法的区别
179
-
180
- `dsl.if()` 提供两种使用方式,根据参数类型自动选择:
181
-
182
- | 方式 | 参数类型 | 执行时机 | 用途 | 示例 |
183
- |------|---------|---------|------|------|
184
- | **方式一** | 字符串 | Schema 定义时 | 静态布尔条件 | `dsl.if('isVip', thenSchema, elseSchema)` |
185
- | **方式二** | 函数 | 验证时 | 动态条件判断 | `dsl.if((data) => data.age >= 18).then(...)` |
186
-
187
- **方式一**(字段条件):基于字段值的静态判断
188
- ```javascript
189
- // 示例:根据 isVip 字段值选择不同的验证规则
190
- dsl.if('isVip', 'number:0-50', 'number:0-10')
191
- ```
192
-
193
- **方式二**(函数条件):基于完整数据的动态判断
194
- ```javascript
195
- // 示例:根据多个字段的组合逻辑动态选择
196
- dsl.if((data) => data.age >= 18 && data.role === 'admin')
197
- .then('email!')
198
- .else('email')
199
- ```
200
-
201
- 此外,`dsl.match()` 适用于多值映射场景:
202
- ```javascript
203
- // 示例:根据 type 字段值映射不同验证规则
204
- dsl.match('type', {
205
- email: 'email!',
206
- phone: 'string:11!',
207
- _default: 'string'
208
- })
209
- ```
210
-
211
- ---
212
-
213
- ## 快速开始
214
-
215
- ### 基础用法
216
-
217
- ```javascript
218
- const { dsl, validate } = require('schema-dsl');
219
-
220
- // 方式1:传统方式(需要 validate 函数)
221
- const schema1 = dsl({
222
- age: 'number!',
223
- status: dsl.if((data) => data.age < 18)
224
- .message('未成年用户不能注册')
225
- });
226
-
227
- validate(schema1, { age: 16, status: 'active' });
228
- // => { valid: false, errors: [{ message: '未成年用户不能注册' }], data: { age: 16, status: 'active' } }
229
-
230
- // ✅ 方式2:快捷方式(一行代码验证)
231
- const result = dsl.if((data) => data.age < 18)
232
- .message('未成年用户不能注册')
233
- .validate({ age: 16 });
234
- // => { valid: false, errors: [{ message: '未成年用户不能注册' }], data: { age: 16 } }
235
-
236
- // ✅ 方式3:.check() 快速判断
237
- const isValid = dsl.if((data) => data.age < 18)
238
- .message('未成年用户不能注册')
239
- .check({ age: 16 });
240
- // => false
241
-
242
- // 2. 条件 + then/else(动态Schema)
243
- const result = dsl.if((data) => data.userType === 'admin')
244
- .then('email!') // 管理员必填
245
- .else('email') // 普通用户可选
246
- .validate({ userType: 'admin', email: 'admin@example.com' });
247
-
248
- // 3. else 可选
249
- const result = dsl.if((data) => data.userType === 'vip')
250
- .then('enum:gold|silver|bronze!')
251
- // 不写 else,非 vip 用户不验证
252
- .validate({ userType: 'user' });
253
-
254
- // 4. 复用验证器
255
- const ageValidator = dsl.if(d => d.age < 18).message('未成年用户不能注册');
256
- const r1 = ageValidator.validate({ age: 16 }); // 失败
257
- const r2 = ageValidator.validate({ age: 20 }); // 通过
258
- ```
259
-
260
- ### 多条件组合
261
-
262
- ```javascript
263
- // 1. AND 条件
264
- const result = dsl.if((data) => data.age >= 18)
265
- .and((data) => data.userType === 'admin')
266
- .message('只有成年管理员可以操作')
267
- .validate({ age: 20, userType: 'user' });
268
-
269
- // 2. OR 条件
270
- const result = dsl.if((data) => data.age < 18)
271
- .or((data) => data.status === 'blocked')
272
- .message('不允许注册')
273
- .validate({ age: 16, status: 'active' });
274
-
275
- // 3. 复杂组合
276
- const result = dsl.if((data) => data.age >= 18)
277
- .and((data) => data.userType === 'admin')
278
- .or((data) => data.status === 'vip')
279
- .then('email!')
280
- .else('email')
281
- .validate(data);
282
- ```
283
-
284
- ### elseIf 分支
285
-
286
- ```javascript
287
- const validator = dsl.if((data) => data.userType === 'admin')
288
- .then('array<string>!')
289
- .elseIf((data) => data.userType === 'vip')
290
- .then('array<string>')
291
- .elseIf((data) => data.userType === 'user')
292
- .then('array')
293
- .else(null); // 游客不验证
294
-
295
- const r1 = validator.validate({ userType: 'admin', permissions: ['read', 'write'] });
296
- const r2 = validator.validate({ userType: 'vip' });
297
- const r3 = validator.validate({ userType: 'guest' });
298
- ```
299
-
300
- ---
301
-
302
- ## API 参考
303
-
304
- ### dsl.if(condition)
305
-
306
- 创建链式条件构建器。
307
-
308
- **参数**:
309
- - `condition` {Function} - 条件函数,接收完整数据对象
310
- - 参数: `(data: any) => boolean`
311
- - 返回: `boolean` - true 表示条件满足
312
-
313
- **返回**: `ConditionalBuilder` - 构建器实例
314
-
315
- **示例**:
316
- ```javascript
317
- dsl.if((data) => data.age >= 18)
318
- dsl.if((data) => data.userType === 'admin')
319
- dsl.if((data) => data.status === 'active' && data.verified)
320
- ```
321
-
322
- ---
323
-
324
- ### .and(condition)
325
-
326
- 添加 AND 条件(与前一个条件组合)。
327
-
328
- > **v1.1.1+** 支持在 `.and()` 后调用 `.message()` 设置独立的错误消息
329
-
330
- **参数**:
331
- - `condition` {Function} - 条件函数
332
-
333
- **返回**: `this` - 支持链式调用
334
-
335
- **基础示例**:
336
- ```javascript
337
- // 传统用法:所有条件共享一个消息
338
- dsl.if((data) => data.age >= 18)
339
- .and((data) => data.userType === 'admin')
340
- .message('不符合条件')
341
- ```
342
-
343
- **v1.1.1+ 独立消息**:
344
- ```javascript
345
- // ✅ 每个条件都有自己的错误消息
346
- dsl.if((data) => !data)
347
- .message('账户不存在')
348
- .and((data) => data.balance < 100)
349
- .message('余额不足')
350
- .assert(account);
351
-
352
- // 工作原理:
353
- // - 第一个条件为 true → 返回 '账户不存在'
354
- // - 第二个条件为 true → 返回 '余额不足'
355
- // - 所有条件为 false → 验证成功
356
- ```
357
-
358
- **多个 .and() 条件**:
359
- ```javascript
360
- // 支持多个 .and() 条件,每个都有独立消息
361
- dsl.if(d => !d)
362
- .message('NOT_FOUND')
363
- .and(d => d.status !== 'active')
364
- .message('INACTIVE')
365
- .and(d => d.balance < 100)
366
- .message('INSUFFICIENT')
367
- .assert(account);
368
-
369
- // 依次检查,第一个为 true 的条件返回其消息
370
- ```
371
-
372
- **逻辑**:
373
- - 传统模式:`(condition1 AND condition2)` - 所有条件为 true 才失败
374
- - 链式检查模式 (v1.1.1+):依次检查,第一个为 true 的失败
375
-
376
- **链式检查模式触发条件**:
377
- 1. 使用 `.message()` 模式
378
- 2. root 条件有 `.message()`
379
- 3. 有 `.and()` 条件
380
- 4. 没有 `.or()` 条件
381
-
382
- ---
383
-
384
- ### .or(condition)
385
-
386
- 添加 OR 条件(与前一个条件组合)。
387
-
388
- > **v1.1.1+** 支持在 `.or()` 后调用 `.message()` 设置独立的错误消息
389
-
390
- **参数**:
391
- - `condition` {Function} - 条件函数
392
-
393
- **返回**: `this` - 支持链式调用
394
-
395
- **基础示例**:
396
- ```javascript
397
- // 传统用法:所有条件共享一个消息
398
- dsl.if((data) => data.age < 18)
399
- .or((data) => data.status === 'blocked')
400
- .message('不允许注册')
401
- ```
402
-
403
- **v1.1.1+ 独立消息**:
404
- ```javascript
405
- // ✅ 每个 OR 条件都有自己的错误消息
406
- dsl.if(d => d.age < 18)
407
- .message('未成年用户不能注册')
408
- .or(d => d.isBlocked)
409
- .message('账户已被封禁')
410
- .assert(data);
411
-
412
- // 工作原理:
413
- // - 第一个条件为 true → 返回 '未成年用户不能注册'
414
- // - 第二个条件为 true → 返回 '账户已被封禁'
415
- // - 所有条件为 false → 验证成功
416
- ```
417
-
418
- **逻辑**: `(condition1 OR condition2)` - 任一条件为 true 就失败
419
-
420
- **注意**:
421
- - 如果有 `.or()` 条件,不会启用链式检查模式
422
- - 使用传统 AND/OR 组合逻辑
423
-
424
- ---
425
-
426
- ### .build()
427
-
428
- 将当前 `ConditionalBuilder` 输出为可直接交给 `Validator` / `validate()` 使用的 schema 对象。
429
-
430
- `.build()` 是 `.toSchema()` 的别名,适合在你想显式拿到最终 schema 时使用。
431
-
432
- ```javascript
433
- const { dsl, validate } = require('schema-dsl');
434
-
435
- const conditionalSchema = dsl.if(data => data.age >= 18)
436
- .then('email!')
437
- .else('email')
438
- .build();
439
-
440
- const result = validate(conditionalSchema, 'user@example.com');
441
- console.log(result.valid);
442
- ```
443
-
444
- ---
445
-
446
- ### .elseIf(condition)
447
-
448
- 添加 else-if 分支。
449
-
450
- **参数**:
451
- - `condition` {Function} - 条件函数
452
-
453
- **返回**: `this` - 支持链式调用
454
-
455
- **示例**:
456
- ```javascript
457
- dsl.if((data) => data.userType === 'admin')
458
- .then('email!')
459
- .elseIf((data) => data.userType === 'vip')
460
- .then('email')
461
- .else(null)
462
- ```
463
-
464
- **注意**: 必须在 `.if()` 之后调用
465
-
466
- ---
467
-
468
- ### .message(msg)
469
-
470
- 设置错误消息(支持多语言 key)。
471
-
472
- > **v1.1.1+** 支持为 `.and()` 和 `.or()` 条件设置独立消息
473
-
474
- **参数**:
475
- - `msg` {string} - 错误消息或多语言 key
476
-
477
- **返回**: `this` - 支持链式调用
478
-
479
- **行为**: 条件为 true 时自动抛出此错误(无需 `.throwError()`)
480
-
481
- **基础示例**:
482
- ```javascript
483
- dsl.if((data) => data.age < 18)
484
- .message('未成年用户不能注册')
485
-
486
- // 支持多语言 key
487
- dsl.if((data) => data.age < 18)
488
- .message('error.underage')
489
- ```
490
-
491
- **v1.1.1+ 为 .and() 设置独立消息**:
492
- ```javascript
493
- // ✅ 每个条件都有自己的错误消息
494
- dsl.if((data) => !data)
495
- .message('账户不存在')
496
- .and((data) => data.balance < 100)
497
- .message('余额不足')
498
- .assert(account);
499
- ```
500
-
501
- **v1.1.1+ 为 .or() 设置独立消息**:
502
- ```javascript
503
- // ✅ OR 条件也支持独立消息
504
- dsl.if(d => d.age < 18)
505
- .message('未成年')
506
- .or(d => d.isBlocked)
507
- .message('已封禁')
508
- .assert(data);
509
- ```
510
-
511
- **链式检查模式说明** (v1.1.1+):
512
-
513
- 当满足以下条件时,自动启用链式检查模式:
514
- 1. 使用 `.message()` 模式(不是 `.then()`/`.else()`)
515
- 2. root 条件有 `.message()`
516
- 3. 有 `.and()` 条件
517
- 4. 没有 `.or()` 条件
518
-
519
- ```javascript
520
- // ✅ 启用链式检查(纯 AND 场景)
521
- dsl.if(d => !d).message('A').and(d => d < 100).message('B')
522
-
523
- // ❌ 不启用(有 .or())
524
- dsl.if(d => !d).message('A').and(d => d < 100).or(d => d > 200).message('B')
525
-
526
- // ❌ 不启用(使用 .then()/.else())
527
- dsl.if(d => d.age >= 18).and(d => d.role === 'admin').then('email!')
528
- ```
529
-
530
- ---
531
-
532
- ### .then(schema)
533
-
534
- 设置满足条件时的 Schema。
535
-
536
- **参数**:
537
- - `schema` {string|DslBuilder|JSONSchema} - DSL 字符串或 Schema 对象
538
-
539
- **返回**: `this` - 支持链式调用
540
-
541
- **示例**:
542
- ```javascript
543
- // DSL 字符串
544
- dsl.if((data) => data.userType === 'admin')
545
- .then('email!')
546
-
547
- // DslBuilder 实例
548
- dsl.if((data) => data.userType === 'admin')
549
- .then(dsl('email!').label('管理员邮箱'))
550
-
551
- // JSON Schema 对象
552
- dsl.if((data) => data.userType === 'admin')
553
- .then({ type: 'string', format: 'email' })
554
- ```
555
-
556
- ---
557
-
558
- ### .else(schema)
559
-
560
- 设置默认 Schema(所有条件都不满足时)。
561
-
562
- **参数**:
563
- - `schema` {string|DslBuilder|JSONSchema|null} - DSL 字符串、Schema 对象或 null
564
-
565
- **返回**: `this` - 支持链式调用
566
-
567
- **特性**: 可选,不写 else 就不验证
568
-
569
- **示例**:
570
- ```javascript
571
- // 显式指定 else
572
- dsl.if((data) => data.userType === 'admin')
573
- .then('email!')
574
- .else('email')
575
-
576
- // else 为 null(显式跳过验证)
577
- dsl.if((data) => data.userType === 'admin')
578
- .then('email!')
579
- .else(null)
580
-
581
- // 不写 else(隐式跳过验证)
582
- dsl.if((data) => data.userType === 'admin')
583
- .then('email!')
584
- ```
585
-
586
- ---
587
-
588
- ### .validate(data, options)
589
-
590
- 快捷验证方法 - 返回完整验证结果。
591
-
592
- **参数**:
593
- - `data` {*} - 待验证的数据(任意类型)
594
- - `options` {Object} - 验证选项(可选)
595
- - `locale` {string} - 语言环境(如 'zh-CN', 'en-US')
596
- - `messages` {Object} - 自定义错误消息
597
-
598
- **返回**: `Object` - 验证结果 `{ valid, errors, data }`
599
-
600
- **特性**: 一行代码完成验证,无需外部 `validate()` 函数
601
-
602
- **示例**:
603
- ```javascript
604
- // 一行代码验证
605
- const result = dsl.if(d => d.age < 18)
606
- .message('未成年用户不能注册')
607
- .validate({ age: 16 });
608
- // => { valid: false, errors: [...], data }
609
-
610
- // 复用验证器
611
- const ageValidator = dsl.if(d => d.age < 18).message('未成年');
612
- const r1 = ageValidator.validate({ age: 16 }); // false
613
- const r2 = ageValidator.validate({ age: 20 }); // true
614
-
615
- // 支持验证选项
616
- const result = dsl.if(d => d.age < 18)
617
- .message('conditional.underAge')
618
- .validate({ age: 16 }, { locale: 'zh-CN' });
619
-
620
- // 验证非对象类型
621
- const result = dsl.if(d => d.includes('@'))
622
- .then('email!')
623
- .validate('test@example.com');
624
- ```
625
-
626
- ---
627
-
628
- ### .validateAsync(data, options)
629
-
630
- 异步验证方法 - 失败自动抛出异常。
631
-
632
- **参数**:
633
- - `data` {*} - 待验证的数据
634
- - `options` {Object} - 验证选项(可选)
635
-
636
- **返回**: `Promise<*>` - 验证通过返回数据,失败抛出异常
637
-
638
- **抛出**: `ValidationError` - 验证失败抛出异常
639
-
640
- **特性**: 适合 async/await 场景,失败自动抛错
641
-
642
- **示例**:
643
- ```javascript
644
- // 异步验证,失败自动抛错
645
- try {
646
- const data = await dsl.if(d => d.age < 18)
647
- .message('未成年用户不能注册')
648
- .validateAsync({ age: 16 });
649
- } catch (error) {
650
- console.log(error.message); // "未成年用户不能注册"
651
- console.log(error.errors); // 详细错误信息
652
- }
653
-
654
- // Express 中间件
655
- app.post('/register', async (req, res, next) => {
656
- try {
657
- await dsl.if(d => d.age < 18)
658
- .message('未成年用户不能注册')
659
- .validateAsync(req.body);
660
-
661
- // 验证通过,继续处理...
662
- const user = await createUser(req.body);
663
- res.json(user);
664
- } catch (error) {
665
- next(error);
666
- }
667
- });
668
-
669
- // 复用验证器
670
- const ageValidator = dsl.if(d => d.age < 18).message('未成年');
671
-
672
- try {
673
- await ageValidator.validateAsync({ age: 16 });
674
- } catch (error) {
675
- // 处理错误
676
- }
677
- ```
678
-
679
- ---
680
-
681
- ### .assert(data, options)
682
-
683
- 断言方法 - 同步验证,失败直接抛错。
684
-
685
- **参数**:
686
- - `data` {*} - 待验证的数据
687
- - `options` {Object} - 验证选项(可选)
688
-
689
- **返回**: `*` - 验证通过返回数据
690
-
691
- **抛出**: `ValidationError` - 验证失败时直接抛出 `ValidationError`
692
-
693
- **特性**: 同步版本的断言验证,适合快速失败场景
694
-
695
- **示例**:
696
- ```javascript
697
- // 断言验证,失败直接抛错
698
- try {
699
- dsl.if(d => d.age < 18)
700
- .message('未成年用户不能注册')
701
- .assert({ age: 16 });
702
- } catch (error) {
703
- console.log(error.message); // "未成年用户不能注册"
704
- }
705
-
706
- // 函数中快速断言
707
- function registerUser(userData) {
708
- // 断言验证
709
- dsl.if(d => d.age < 18)
710
- .message('未成年用户不能注册')
711
- .assert(userData);
712
-
713
- dsl.if(d => !d.email)
714
- .message('邮箱不能为空')
715
- .assert(userData);
716
-
717
- // 验证通过,继续处理...
718
- return createUser(userData);
719
- }
720
-
721
- // 链式断言
722
- function validateAndCreate(data) {
723
- dsl.if(d => d.age < 18).message('未成年').assert(data);
724
- dsl.if(d => !d.email).message('邮箱必填').assert(data);
725
- dsl.if(d => !d.username).message('用户名必填').assert(data);
726
-
727
- return createUser(data);
728
- }
729
- ```
730
-
731
- ---
732
-
733
- ### .check(data)
734
-
735
- 快捷检查方法 - 只返回 boolean。
736
-
737
- **参数**:
738
- - `data` {*} - 待验证的数据
739
-
740
- **返回**: `boolean` - 验证是否通过
741
-
742
- **特性**: 比 `.validate()` 更简洁,适合只需要判断真假的场景
743
-
744
- **示例**:
745
- ```javascript
746
- // 快速判断
747
- const isValid = dsl.if(d => d.age < 18)
748
- .message('未成年')
749
- .check({ age: 16 });
750
- // => false
751
-
752
- // 断言场景
753
- if (!validator.check(userData)) {
754
- console.log('验证失败');
755
- }
756
-
757
- // 循环验证
758
- const users = [{ age: 16 }, { age: 20 }, { age: 17 }];
759
- const adults = users.filter(u =>
760
- !dsl.if(d => d.age < 18).message('未成年').check(u)
761
- );
762
- ```
763
-
764
- ---
765
-
766
- ## 使用场景
767
-
768
- ### 场景1:用户注册 - 快捷验证
769
-
770
- 使用 `.validate()` 方法快速验证用户注册数据。
771
-
772
- ```javascript
773
- // 创建可复用的验证器
774
- const validators = {
775
- age: dsl.if(d => d.age < 18).message('未成年用户不能注册'),
776
- email: dsl.if(d => d.userType === 'admin')
777
- .message('管理员必须提供邮箱')
778
- };
779
-
780
- // 快速验证(一行代码)
781
- function registerUser(userData) {
782
- // 验证年龄
783
- const ageResult = validators.age.validate(userData);
784
- if (!ageResult.valid) {
785
- return { error: ageResult.errors[0].message };
786
- }
787
-
788
- // 验证邮箱
789
- const emailResult = validators.email.validate(userData);
790
- if (!emailResult.valid) {
791
- return { error: emailResult.errors[0].message };
792
- }
793
-
794
- return { success: true };
795
- }
796
-
797
- // 使用
798
- registerUser({ username: 'test', age: 16 });
799
- // => { error: '未成年用户不能注册' }
800
- ```
801
-
802
- ### 场景2:批量数据验证 - 使用 .check()
803
-
804
- 使用 `.check()` 方法快速过滤符合条件的数据。
805
-
806
- ```javascript
807
- const users = [
808
- { name: '张三', age: 16 },
809
- { name: '李四', age: 20 },
810
- { name: '王五', age: 17 },
811
- { name: '赵六', age: 25 }
812
- ];
813
-
814
- // 创建验证器
815
- const canRegister = dsl.if(d => d.age < 18)
816
- .message('未成年');
817
-
818
- // ✅ 使用 .check() 过滤
819
- const validUsers = users.filter(u => !canRegister.check(u));
820
- // => [{ name: '李四', age: 20 }, { name: '赵六', age: 25 }]
821
-
822
- // ✅ 使用 .check() 统计
823
- const minorCount = users.filter(u => canRegister.check(u)).length;
824
- console.log(`未成年用户: ${minorCount} 人`);
825
- // => "未成年用户: 2 人"
826
- ```
827
-
828
- ### 场景3:表单实时验证
829
-
830
- ```javascript
831
- // 前端表单验证
832
- const formValidators = {
833
- username: dsl.if(d => d.length < 3)
834
- .message('用户名至少3个字符'),
835
-
836
- password: dsl.if(d => d.length < 8)
837
- .message('密码至少8个字符')
838
- };
839
-
840
- // 实时验证(输入时)
841
- function onUsernameChange(value) {
842
- const isValid = formValidators.username.check(value);
843
- if (!isValid) {
844
- showError('用户名至少3个字符');
845
- } else {
846
- clearError();
847
- }
848
- }
849
-
850
- // 提交验证
851
- function onSubmit(formData) {
852
- const usernameResult = formValidators.username.validate(formData.username);
853
- const passwordResult = formValidators.password.validate(formData.password);
854
-
855
- if (!usernameResult.valid) {
856
- return alert(usernameResult.errors[0].message);
857
- }
858
-
859
- if (!passwordResult.valid) {
860
- return alert(passwordResult.errors[0].message);
861
- }
862
-
863
- // 提交表单...
864
- }
865
- ```
866
-
867
- ### 场景4:用户权限检查
868
-
869
- ```javascript
870
- // 权限验证器
871
- const hasPermission = dsl.if(d => d.role === 'admin')
872
- .or(d => d.role === 'moderator')
873
- .message('权限不足');
874
-
875
- // 中间件
876
- function checkPermission(req, res, next) {
877
- if (!hasPermission.check(req.user)) {
878
- return res.status(403).json({ error: '权限不足' });
879
- }
880
- next();
881
- }
882
-
883
- // 路由
884
- app.delete('/users/:id', checkPermission, deleteUser);
885
- ```
886
-
887
- ### 场景5:根据年龄和用户类型验证不同字段(传统方式对比)
888
-
889
- ```javascript
890
- // 传统方式(需要 validate 函数)
891
- const schema = dsl({
892
- username: 'string:3-32!',
893
- age: 'number:1-120!',
894
- userType: 'enum:admin|vip|user!',
895
-
896
- // 未成年禁止注册
897
- ageCheck: dsl.if((data) => data.age < 18)
898
- .message('未成年用户不能注册'),
899
-
900
- // 管理员必须有邮箱
901
- email: dsl.if((data) => data.userType === 'admin')
902
- .then('email!')
903
- .else('email'),
904
-
905
- // VIP用户必须有手机号
906
- phone: dsl.if((data) => data.userType === 'vip')
907
- .then('string:11!')
908
- .else(null),
909
-
910
- // 管理员和VIP可以设置昵称
911
- nickname: dsl.if((data) => data.userType === 'admin')
912
- .or((data) => data.userType === 'vip')
913
- .then('string:2-20')
914
- .else(null)
915
- });
916
-
917
- // 测试
918
- validate(schema, {
919
- username: 'admin1',
920
- age: 25,
921
- userType: 'admin',
922
- email: 'admin@example.com'
923
- });
924
- // => { valid: true }
925
- ```
926
-
927
- ### 场景2:商品发布
928
-
929
- 根据商品类型验证不同字段。
930
-
931
- ```javascript
932
- const schema = dsl({
933
- title: 'string:1-100!',
934
- price: 'number:0-!',
935
- type: 'enum:physical|digital|service!',
936
-
937
- // 实体商品需要重量和尺寸
938
- weight: dsl.if((data) => data.type === 'physical')
939
- .then('number:0-!')
940
- .else(null),
941
-
942
- dimensions: dsl.if((data) => data.type === 'physical')
943
- .then('string!')
944
- .else(null),
945
-
946
- // 数字商品需要下载链接
947
- downloadUrl: dsl.if((data) => data.type === 'digital')
948
- .then('url!')
949
- .else(null),
950
-
951
- // 服务类需要服务时长
952
- duration: dsl.if((data) => data.type === 'service')
953
- .then('number:1-!')
954
- .else(null)
955
- });
956
-
957
- // 实体商品
958
- validate(schema, {
959
- title: '笔记本电脑',
960
- price: 5999,
961
- type: 'physical',
962
- weight: 1.5,
963
- dimensions: '30x20x2cm'
964
- });
965
- // => { valid: true }
966
-
967
- // 数字商品
968
- validate(schema, {
969
- title: '电子书',
970
- price: 29.9,
971
- type: 'digital',
972
- downloadUrl: 'https://example.com/download'
973
- });
974
- // => { valid: true }
975
- ```
976
-
977
- ### 场景3:权限控制
978
-
979
- 根据用户角色和状态控制访问。
980
-
981
- ```javascript
982
- const schema = dsl({
983
- userId: 'string!',
984
- role: 'enum:admin|moderator|user!',
985
- status: 'enum:active|suspended|banned!',
986
-
987
- // 被封禁用户禁止操作
988
- accessCheck: dsl.if((data) => data.status === 'banned')
989
- .message('您的账号已被封禁'),
990
-
991
- // 暂停用户只能查看
992
- operationType: dsl.if((data) => data.status === 'suspended')
993
- .then('enum:view!')
994
- .else('enum:view|edit|delete!'),
995
-
996
- // 管理员可以访问所有资源
997
- resourceIds: dsl.if((data) => data.role === 'admin')
998
- .then('array<string>') // 可选
999
- .else('array<string>!') // 必填
1000
- });
1001
- ```
1002
-
1003
- ---
1004
-
1005
- ## 最佳实践
1006
-
1007
- ### 1. 条件函数保持简单
1008
-
1009
- ❌ **不推荐**:
1010
- ```javascript
1011
- dsl.if((data) => {
1012
- const user = getUserFromDB(data.userId); // 同步数据库查询
1013
- return user.level > 5;
1014
- })
1015
- ```
1016
-
1017
- ✅ **推荐**:
1018
- ```javascript
1019
- dsl.if((data) => data.userLevel > 5)
1020
- ```
1021
-
1022
- **原因**: 条件函数应该只读取数据对象,不应该有副作用或执行耗时操作。
1023
-
1024
- ---
1025
-
1026
- ### 2. 使用有意义的字段名
1027
-
1028
- ❌ **不推荐**:
1029
- ```javascript
1030
- const schema = dsl({
1031
- field1: 'string!',
1032
- check1: dsl.if((data) => data.field1 === 'admin')
1033
- .message('Error')
1034
- });
1035
- ```
1036
-
1037
- ✅ **推荐**:
1038
- ```javascript
1039
- const schema = dsl({
1040
- userType: 'string!',
1041
- ageVerification: dsl.if((data) => data.age < 18)
1042
- .message('未成年用户不能注册')
1043
- });
1044
- ```
1045
-
1046
- ---
1047
-
1048
- ### 3. 合理使用 else
1049
-
1050
- 当条件不满足时需要不同的验证规则,使用 `.else()`:
1051
-
1052
- ```javascript
1053
- dsl.if((data) => data.userType === 'admin')
1054
- .then('email!')
1055
- .else('email') // 不同的验证规则
1056
- ```
1057
-
1058
- 当条件不满足时不需要验证,省略 `.else()`:
1059
-
1060
- ```javascript
1061
- dsl.if((data) => data.userType === 'vip')
1062
- .then('string:6!')
1063
- // 不写 else,非 vip 用户不验证
1064
- ```
1065
-
1066
- ---
1067
-
1068
- ### 4. 多条件组合优先使用函数内部逻辑
1069
-
1070
- 简单条件可以直接在函数内部组合:
1071
-
1072
- ```javascript
1073
- // ✅ 推荐(简洁)
1074
- dsl.if((data) => data.age >= 18 && data.userType === 'admin')
1075
- .then('email!')
1076
-
1077
- // ⚠️ 可用但稍繁琐
1078
- dsl.if((data) => data.age >= 18)
1079
- .and((data) => data.userType === 'admin')
1080
- .then('email!')
1081
- ```
1082
-
1083
- 复杂逻辑或需要可维护性时使用 `.and()` / `.or()`:
1084
-
1085
- ```javascript
1086
- // ✅ 推荐(可读性强)
1087
- dsl.if((data) => data.age >= 18)
1088
- .and((data) => data.userType === 'admin')
1089
- .and((data) => data.verified)
1090
- .or((data) => data.isSuperUser)
1091
- .then('email!')
1092
- ```
1093
-
1094
- ---
1095
-
1096
- ### 5. 错误消息清晰明确
1097
-
1098
- ❌ **不推荐**:
1099
- ```javascript
1100
- dsl.if((data) => data.age < 18)
1101
- .message('Error')
1102
- ```
1103
-
1104
- ✅ **推荐**:
1105
- ```javascript
1106
- dsl.if((data) => data.age < 18)
1107
- .message('未成年用户不能注册')
1108
- ```
1109
-
1110
- ✅ **更好**(支持多语言):
1111
- ```javascript
1112
- dsl.if((data) => data.age < 18)
1113
- .message('error.user.underage')
1114
- ```
1115
-
1116
- ---
1117
-
1118
- ## 常见问题
1119
-
1120
- ### Q1: 条件函数什么时候执行?
1121
-
1122
- **A**: 在调用 `validate()` 时执行,不是在定义 Schema 时。
1123
-
1124
- ```javascript
1125
- const schema = dsl({
1126
- email: dsl.if((data) => data.userType === 'admin')
1127
- .then('email!') // ← 这里不会执行
1128
- });
1129
-
1130
- validate(schema, data); // ← 条件函数在这里执行
1131
- ```
1132
-
1133
- ---
1134
-
1135
- ### Q2: 条件函数可以访问哪些数据?
1136
-
1137
- **A**: 可以访问完整的数据对象。
1138
-
1139
- ```javascript
1140
- const schema = dsl({
1141
- age: 'number!',
1142
- userType: 'string!',
1143
- status: 'string!',
1144
- email: dsl.if((data) => {
1145
- // 可以访问所有字段
1146
- return data.age >= 18 && data.userType === 'admin' && data.status === 'active';
1147
- }).then('email!')
1148
- });
1149
- ```
1150
-
1151
- ---
1152
-
1153
- ### Q3: 如何处理条件函数抛错?
1154
-
1155
- **A**: 条件函数抛错会被捕获,视为条件不满足。
1156
-
1157
- ```javascript
1158
- const schema = dsl({
1159
- obj: 'object!',
1160
- result: dsl.if((data) => data.obj.nested.value > 10)
1161
- .then('string!')
1162
- .else(null)
1163
- });
1164
-
1165
- // data.obj.nested 不存在,访问会抛错
1166
- validate(schema, { obj: {} });
1167
- // => { valid: true } 条件不满足,执行 else(null)
1168
- ```
1169
-
1170
- **建议**: 在条件函数中做好防御性检查:
1171
-
1172
- ```javascript
1173
- dsl.if((data) => data.obj?.nested?.value > 10)
1174
- .then('string!')
1175
- ```
1176
-
1177
- ---
1178
-
1179
- ### Q4: 可以嵌套 dsl.if() 吗?
1180
-
1181
- **A**: 可以,支持嵌套。
1182
-
1183
- ```javascript
1184
- const schema = dsl({
1185
- userType: 'string!',
1186
- age: 'number!',
1187
- email: dsl.if((data) => data.userType === 'admin')
1188
- .then(
1189
- dsl.if((data) => data.age >= 18)
1190
- .then('email!')
1191
- .else('email')
1192
- )
1193
- .else('email')
1194
- });
1195
- ```
1196
-
1197
- ---
1198
-
1199
- ### Q5: 如何与现有的 dsl.match() 方法配合使用?
1200
-
1201
- **A**: 可以混用,选择最适合的方法。
1202
-
1203
- ```javascript
1204
- const schema = dsl({
1205
- // 静态值映射 - 使用 match
1206
- userType: 'enum:admin|vip|user!',
1207
- level: dsl.match('userType', {
1208
- admin: 'enum:high!',
1209
- vip: 'enum:medium!',
1210
- user: 'enum:low!'
1211
- }),
1212
-
1213
- // 动态条件判断 - 使用 if
1214
- email: dsl.if((data) => data.userType === 'admin' && data.level === 'high')
1215
- .then('email!')
1216
- .else('email')
1217
- });
1218
- ```
1219
-
1220
- **选择建议**:
1221
- - **简单值映射** → 使用 `dsl.match()`
1222
- - **复杂条件逻辑** → 使用 `dsl.if()`
1223
-
1224
- ---
1225
-
1226
- ### Q6: 是否支持非对象类型(字符串、数组、数字等)?
1227
-
1228
- **A**: 完全支持!可以直接验证任何类型的值。
1229
-
1230
- ```javascript
1231
- // 示例1:直接验证字符串
1232
- const stringSchema = dsl.if((data) => typeof data === 'string' && data.includes('@'))
1233
- .then('email!')
1234
- .else('string:1-50');
1235
-
1236
- validate(stringSchema, 'test@example.com'); // ✅ 作为邮箱验证
1237
- validate(stringSchema, 'just a text'); // ✅ 作为普通字符串验证
1238
-
1239
- // 示例2:直接验证数组
1240
- const arraySchema = dsl.if((data) => Array.isArray(data) && data.length > 5)
1241
- .message('数组最多5个元素');
1242
-
1243
- validate(arraySchema, [1, 2, 3]); // ✅ 通过
1244
- validate(arraySchema, [1, 2, 3, 4, 5, 6]); // ❌ 失败
1245
-
1246
- // 示例3:直接验证数字
1247
- const numberSchema = dsl.if((data) => typeof data === 'number' && data < 0)
1248
- .message('不允许负数');
1249
-
1250
- validate(numberSchema, 10); // ✅ 通过
1251
- validate(numberSchema, -5); // ❌ 失败
1252
-
1253
- // 示例4:自动识别类型(邮箱或手机号)
1254
- const contactSchema = dsl.if((data) => typeof data === 'string' && data.includes('@'))
1255
- .then('email!')
1256
- .else('string:11!');
1257
-
1258
- validate(contactSchema, 'user@example.com'); // ✅ 作为邮箱验证
1259
- validate(contactSchema, '13800138000'); // ✅ 作为手机号验证
1260
- ```
1261
-
1262
- **完整示例**: 参见 `test/unit/conditional-non-object.test.ts`
1263
-
1264
- ---
1265
-
1266
- ### Q7: 性能如何?
1267
-
1268
- **A**: 性能优秀,条件函数执行非常快。
1269
-
1270
- - 条件函数是纯 JavaScript 函数,执行速度快
1271
- - 只遍历条件链一次,找到第一个匹配的条件就停止
1272
- - 支持缓存优化(WeakMap)
1273
-
1274
- **性能提示**:
1275
- - 避免在条件函数中执行耗时操作(数据库查询、API 调用)
1276
- - 将最常见的条件放在前面(if 而不是 elseIf)
1277
-
1278
- ---
1279
-
1280
- ## 更新日志
1281
-
1282
- ### v1.1.1 (2026-01-05)
1283
-
1284
- - ✅ 新增 `ConditionalBuilder` 类
1285
- - ✅ 新增 `dsl.if()` 链式条件 API
1286
- - ✅ 支持 and/or 多条件组合
1287
- - ✅ 支持 elseIf 多分支
1288
- - ✅ message 自动抛错(无需 throwError)
1289
- - ✅ else 可选(不写就不验证)
1290
- - ❌ 移除无效的旧条件方法类型定义
1291
-
1292
- ---
1293
-
1294
- ## 相关文档
1295
-
1296
- - [快速开始](./quick-start.md)
1297
- - [验证指南](./validation-guide.md)
1298
- - [API 参考](./api-reference.md)
1299
- - [最佳实践](./best-practices.md)
1300
-
1301
- ---
1302
-
1303
- ## 对应示例文件
1304
-
1305
- **示例入口**: [conditional-api.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/conditional-api.ts)
1306
- **说明**: 同时覆盖失败谓词模式下的 `.check()` / `.assert()`,以及字段名版本 `dsl.if(field, then, else)` 和 `dsl.match()` 映射。
1307
-
1
+ # 链式条件 API - ConditionalBuilder
2
+
3
+ > **版本**: schema-dsl v2.0.0-beta.2
4
+ > **更新日期**: 2026-05-08
5
+ > **状态**: ✅ 稳定
6
+
7
+ ---
8
+
9
+ ## 📋 目录
10
+
11
+ - [概述](#概述)
12
+ - [🆕 v1.1.1 新功能](#-v110-新功能)
13
+ - [快速开始](#快速开始)
14
+ - [API 参考](#api-参考)
15
+ - [使用场景](#使用场景)
16
+ - [最佳实践](#最佳实践)
17
+ - [常见问题](#常见问题)
18
+
19
+ ---
20
+
21
+ ## 概述
22
+
23
+ `ConditionalBuilder` 提供流畅的链式条件判断 API,类似 JavaScript 的 if-else 语句,用于在验证时根据实际数据动态调整验证规则。
24
+
25
+ > 关键语义:当你使用 `.message()` / `.assert()` / `.check()` 这类“失败即返回”的模式时,条件函数应该写成**失败条件**,因为条件返回 `true` 才会被判定为失败。
26
+
27
+ ### 核心特性
28
+
29
+ - ✅ **链式调用** - 流畅的 API,类似 JavaScript if-else
30
+ - ✅ **运行时执行** - 在验证时根据实际数据判断
31
+ - ✅ **多条件组合** - 支持 and/or 逻辑组合
32
+ - ✅ **🆕 独立消息** - v1.1.1+ 每个 .and()/.or() 可有独立错误消息
33
+ - ✅ **else 可选** - 不写 else 就不验证
34
+ - ✅ **简化设计** - message 自动抛错,无需 throwError()
35
+ - ✅ **完全兼容** - 不影响现有 API
36
+
37
+ ---
38
+
39
+ ## 🆕 v1.1.1 新功能
40
+
41
+ ### 独立消息支持 - `.and()/.or()` 后可调用 `.message()`
42
+
43
+ **每个条件都可以有自己的错误消息**
44
+
45
+ v1.1.1 开始,支持在 `.and()` 和 `.or()` 后调用 `.message()` 设置独立的错误消息,让错误提示更精确。
46
+
47
+ #### 基础用法
48
+
49
+ ```javascript
50
+ const { dsl } = require('schema-dsl');
51
+
52
+ // ✅ v1.1.1+ 新功能:每个条件独立消息
53
+ dsl.if(d => !d)
54
+ .message('ACCOUNT_NOT_FOUND')
55
+ .and(d => d.tradable_credits < amount)
56
+ .message('INSUFFICIENT_TRADABLE_CREDITS')
57
+ .assert(account);
58
+
59
+ // 工作原理:
60
+ // - 第一个条件为 true → 返回 'ACCOUNT_NOT_FOUND'
61
+ // - 第二个条件为 true → 返回 'INSUFFICIENT_TRADABLE_CREDITS'
62
+ // - 所有条件为 false → 验证成功
63
+ ```
64
+
65
+ #### 多个 .and() 条件
66
+
67
+ ```javascript
68
+ // 多层验证,每层都有清晰的错误消息
69
+ dsl.if(d => !d)
70
+ .message('ACCOUNT_NOT_FOUND')
71
+ .and(d => d.status !== 'active')
72
+ .message('ACCOUNT_INACTIVE')
73
+ .and(d => d.tradable_credits < amount)
74
+ .message('INSUFFICIENT_TRADABLE_CREDITS')
75
+ .assert(account);
76
+
77
+ // 依次检查,第一个为 true 的条件返回其消息
78
+ ```
79
+
80
+ #### .or() 条件独立消息
81
+
82
+ ```javascript
83
+ // OR 条件也支持独立消息
84
+ dsl.if(d => d.age < 18)
85
+ .message('未成年用户不能注册')
86
+ .or(d => d.isBlocked)
87
+ .message('账户已被封禁')
88
+ .assert(data);
89
+
90
+ // 任一条件为 true 就失败,返回对应消息
91
+ ```
92
+
93
+ #### 链式检查模式
94
+
95
+ v1.1.1 引入了**链式检查模式**,当满足以下条件时自动启用:
96
+
97
+ 1. 使用 `.message()` 模式(不是 `.then()`/`.else()`)
98
+ 2. root 条件有 `.message()`
99
+ 3. 有 `.and()` 条件
100
+ 4. 没有 `.or()` 条件
101
+
102
+ **链式检查模式特点**:
103
+ - 依次检查每个条件
104
+ - 第一个为 `true` 的条件失败,返回其消息
105
+ - 所有条件为 `false` 时验证通过
106
+
107
+ **示例对比**:
108
+
109
+ ```javascript
110
+ // ✅ 启用链式检查(纯 AND 场景)
111
+ dsl.if(d => !d).message('A').and(d => d < 100).message('B')
112
+
113
+ // ❌ 不启用(有 .or(),使用传统 AND/OR 逻辑)
114
+ dsl.if(d => !d).message('A').and(d => d < 100).or(d => d > 200).message('B')
115
+
116
+ // ❌ 不启用(使用 .then()/.else(),不是 message 模式)
117
+ dsl.if(d => d.age >= 18).and(d => d.role === 'admin').then('email!')
118
+ ```
119
+
120
+ #### 向后兼容性
121
+
122
+ **100% 向后兼容**,不影响现有代码:
123
+
124
+ ```javascript
125
+ // ✅ 原有用法继续工作
126
+ dsl.if(d => d.age >= 18).and(d => d.role === 'admin').message('不符合条件')
127
+
128
+ // ✅ .and() 后不调用 .message() 也可以
129
+ dsl.if(d => !d).message('整体错误').and(d => d < 100).assert(50)
130
+ // → 使用整体消息 '整体错误'
131
+ ```
132
+
133
+ #### 实际应用场景
134
+
135
+ **场景1:账户验证**
136
+ ```javascript
137
+ function validateAccount(account, amount) {
138
+ dsl.if(d => !d)
139
+ .message('ACCOUNT_NOT_FOUND')
140
+ .and(d => d.status !== 'active')
141
+ .message('ACCOUNT_INACTIVE')
142
+ .and(d => d.balance < amount)
143
+ .message('INSUFFICIENT_BALANCE')
144
+ .assert(account);
145
+ }
146
+
147
+ // 每个失败点都有清晰的错误消息
148
+ ```
149
+
150
+ **场景2:用户权限验证**
151
+ ```javascript
152
+ function validateUserPermission(user) {
153
+ dsl.if(d => d.role !== 'admin')
154
+ .message('NO_ADMIN_PERMISSION')
155
+ .and(d => !d.isVerified)
156
+ .message('USER_NOT_VERIFIED')
157
+ .and(d => d.isBanned)
158
+ .message('USER_BANNED')
159
+ .assert(user);
160
+ }
161
+ ```
162
+
163
+ **场景3:订单状态检查**
164
+ ```javascript
165
+ function validateOrder(order) {
166
+ dsl.if(d => d.status !== 'paid')
167
+ .message('ORDER_NOT_PAID')
168
+ .and(d => !d.payment)
169
+ .message('PAYMENT_INFO_MISSING')
170
+ .and(d => !d.shippingAddress)
171
+ .message('SHIPPING_ADDRESS_MISSING')
172
+ .assert(order);
173
+ }
174
+ ```
175
+
176
+ ---
177
+
178
+ ## 与现有方法的区别
179
+
180
+ `dsl.if()` 提供两种使用方式,根据参数类型自动选择:
181
+
182
+ | 方式 | 参数类型 | 执行时机 | 用途 | 示例 |
183
+ |------|---------|---------|------|------|
184
+ | **方式一** | 字符串 | Schema 定义时 | 静态布尔条件 | `dsl.if('isVip', thenSchema, elseSchema)` |
185
+ | **方式二** | 函数 | 验证时 | 动态条件判断 | `dsl.if((data) => data.age >= 18).then(...)` |
186
+
187
+ **方式一**(字段条件):基于字段值的静态判断
188
+ ```javascript
189
+ // 示例:根据 isVip 字段值选择不同的验证规则
190
+ dsl.if('isVip', 'number:0-50', 'number:0-10')
191
+ ```
192
+
193
+ **方式二**(函数条件):基于完整数据的动态判断
194
+ ```javascript
195
+ // 示例:根据多个字段的组合逻辑动态选择
196
+ dsl.if((data) => data.age >= 18 && data.role === 'admin')
197
+ .then('email!')
198
+ .else('email')
199
+ ```
200
+
201
+ 此外,`dsl.match()` 适用于多值映射场景:
202
+ ```javascript
203
+ // 示例:根据 type 字段值映射不同验证规则
204
+ dsl.match('type', {
205
+ email: 'email!',
206
+ phone: 'string:11!',
207
+ _default: 'string'
208
+ })
209
+ ```
210
+
211
+ ---
212
+
213
+ ## 快速开始
214
+
215
+ ### 基础用法
216
+
217
+ ```javascript
218
+ const { dsl, validate } = require('schema-dsl');
219
+
220
+ // 方式1:传统方式(需要 validate 函数)
221
+ const schema1 = dsl({
222
+ age: 'number!',
223
+ status: dsl.if((data) => data.age < 18)
224
+ .message('未成年用户不能注册')
225
+ });
226
+
227
+ validate(schema1, { age: 16, status: 'active' });
228
+ // => { valid: false, errors: [{ message: '未成年用户不能注册' }], data: { age: 16, status: 'active' } }
229
+
230
+ // ✅ 方式2:快捷方式(一行代码验证)
231
+ const result = dsl.if((data) => data.age < 18)
232
+ .message('未成年用户不能注册')
233
+ .validate({ age: 16 });
234
+ // => { valid: false, errors: [{ message: '未成年用户不能注册' }], data: { age: 16 } }
235
+
236
+ // ✅ 方式3:.check() 快速判断
237
+ const isValid = dsl.if((data) => data.age < 18)
238
+ .message('未成年用户不能注册')
239
+ .check({ age: 16 });
240
+ // => false
241
+
242
+ // 2. 条件 + then/else(动态Schema)
243
+ const result = dsl.if((data) => data.userType === 'admin')
244
+ .then('email!') // 管理员必填
245
+ .else('email') // 普通用户可选
246
+ .validate({ userType: 'admin', email: 'admin@example.com' });
247
+
248
+ // 3. else 可选
249
+ const result = dsl.if((data) => data.userType === 'vip')
250
+ .then('enum:gold|silver|bronze!')
251
+ // 不写 else,非 vip 用户不验证
252
+ .validate({ userType: 'user' });
253
+
254
+ // 4. 复用验证器
255
+ const ageValidator = dsl.if(d => d.age < 18).message('未成年用户不能注册');
256
+ const r1 = ageValidator.validate({ age: 16 }); // 失败
257
+ const r2 = ageValidator.validate({ age: 20 }); // 通过
258
+ ```
259
+
260
+ ### 多条件组合
261
+
262
+ ```javascript
263
+ // 1. AND 条件
264
+ const result = dsl.if((data) => data.age >= 18)
265
+ .and((data) => data.userType === 'admin')
266
+ .message('只有成年管理员可以操作')
267
+ .validate({ age: 20, userType: 'user' });
268
+
269
+ // 2. OR 条件
270
+ const result = dsl.if((data) => data.age < 18)
271
+ .or((data) => data.status === 'blocked')
272
+ .message('不允许注册')
273
+ .validate({ age: 16, status: 'active' });
274
+
275
+ // 3. 复杂组合
276
+ const result = dsl.if((data) => data.age >= 18)
277
+ .and((data) => data.userType === 'admin')
278
+ .or((data) => data.status === 'vip')
279
+ .then('email!')
280
+ .else('email')
281
+ .validate(data);
282
+ ```
283
+
284
+ ### elseIf 分支
285
+
286
+ ```javascript
287
+ const validator = dsl.if((data) => data.userType === 'admin')
288
+ .then('array<string>!')
289
+ .elseIf((data) => data.userType === 'vip')
290
+ .then('array<string>')
291
+ .elseIf((data) => data.userType === 'user')
292
+ .then('array')
293
+ .else(null); // 游客不验证
294
+
295
+ const r1 = validator.validate({ userType: 'admin', permissions: ['read', 'write'] });
296
+ const r2 = validator.validate({ userType: 'vip' });
297
+ const r3 = validator.validate({ userType: 'guest' });
298
+ ```
299
+
300
+ ---
301
+
302
+ ## API 参考
303
+
304
+ ### dsl.if(condition)
305
+
306
+ 创建链式条件构建器。
307
+
308
+ **参数**:
309
+ - `condition` {Function} - 条件函数,接收完整数据对象
310
+ - 参数: `(data: any) => boolean`
311
+ - 返回: `boolean` - true 表示条件满足
312
+
313
+ **返回**: `ConditionalBuilder` - 构建器实例
314
+
315
+ **示例**:
316
+ ```javascript
317
+ dsl.if((data) => data.age >= 18)
318
+ dsl.if((data) => data.userType === 'admin')
319
+ dsl.if((data) => data.status === 'active' && data.verified)
320
+ ```
321
+
322
+ ---
323
+
324
+ ### .and(condition)
325
+
326
+ 添加 AND 条件(与前一个条件组合)。
327
+
328
+ > **v1.1.1+** 支持在 `.and()` 后调用 `.message()` 设置独立的错误消息
329
+
330
+ **参数**:
331
+ - `condition` {Function} - 条件函数
332
+
333
+ **返回**: `this` - 支持链式调用
334
+
335
+ **基础示例**:
336
+ ```javascript
337
+ // 传统用法:所有条件共享一个消息
338
+ dsl.if((data) => data.age >= 18)
339
+ .and((data) => data.userType === 'admin')
340
+ .message('不符合条件')
341
+ ```
342
+
343
+ **v1.1.1+ 独立消息**:
344
+ ```javascript
345
+ // ✅ 每个条件都有自己的错误消息
346
+ dsl.if((data) => !data)
347
+ .message('账户不存在')
348
+ .and((data) => data.balance < 100)
349
+ .message('余额不足')
350
+ .assert(account);
351
+
352
+ // 工作原理:
353
+ // - 第一个条件为 true → 返回 '账户不存在'
354
+ // - 第二个条件为 true → 返回 '余额不足'
355
+ // - 所有条件为 false → 验证成功
356
+ ```
357
+
358
+ **多个 .and() 条件**:
359
+ ```javascript
360
+ // 支持多个 .and() 条件,每个都有独立消息
361
+ dsl.if(d => !d)
362
+ .message('NOT_FOUND')
363
+ .and(d => d.status !== 'active')
364
+ .message('INACTIVE')
365
+ .and(d => d.balance < 100)
366
+ .message('INSUFFICIENT')
367
+ .assert(account);
368
+
369
+ // 依次检查,第一个为 true 的条件返回其消息
370
+ ```
371
+
372
+ **逻辑**:
373
+ - 传统模式:`(condition1 AND condition2)` - 所有条件为 true 才失败
374
+ - 链式检查模式 (v1.1.1+):依次检查,第一个为 true 的失败
375
+
376
+ **链式检查模式触发条件**:
377
+ 1. 使用 `.message()` 模式
378
+ 2. root 条件有 `.message()`
379
+ 3. 有 `.and()` 条件
380
+ 4. 没有 `.or()` 条件
381
+
382
+ ---
383
+
384
+ ### .or(condition)
385
+
386
+ 添加 OR 条件(与前一个条件组合)。
387
+
388
+ > **v1.1.1+** 支持在 `.or()` 后调用 `.message()` 设置独立的错误消息
389
+
390
+ **参数**:
391
+ - `condition` {Function} - 条件函数
392
+
393
+ **返回**: `this` - 支持链式调用
394
+
395
+ **基础示例**:
396
+ ```javascript
397
+ // 传统用法:所有条件共享一个消息
398
+ dsl.if((data) => data.age < 18)
399
+ .or((data) => data.status === 'blocked')
400
+ .message('不允许注册')
401
+ ```
402
+
403
+ **v1.1.1+ 独立消息**:
404
+ ```javascript
405
+ // ✅ 每个 OR 条件都有自己的错误消息
406
+ dsl.if(d => d.age < 18)
407
+ .message('未成年用户不能注册')
408
+ .or(d => d.isBlocked)
409
+ .message('账户已被封禁')
410
+ .assert(data);
411
+
412
+ // 工作原理:
413
+ // - 第一个条件为 true → 返回 '未成年用户不能注册'
414
+ // - 第二个条件为 true → 返回 '账户已被封禁'
415
+ // - 所有条件为 false → 验证成功
416
+ ```
417
+
418
+ **逻辑**: `(condition1 OR condition2)` - 任一条件为 true 就失败
419
+
420
+ **注意**:
421
+ - 如果有 `.or()` 条件,不会启用链式检查模式
422
+ - 使用传统 AND/OR 组合逻辑
423
+
424
+ ---
425
+
426
+ ### .build()
427
+
428
+ 将当前 `ConditionalBuilder` 输出为可直接交给 `Validator` / `validate()` 使用的 schema 对象。
429
+
430
+ `.build()` 是 `.toSchema()` 的别名,适合在你想显式拿到最终 schema 时使用。
431
+
432
+ ```javascript
433
+ const { dsl, validate } = require('schema-dsl');
434
+
435
+ const conditionalSchema = dsl.if(data => data.age >= 18)
436
+ .then('email!')
437
+ .else('email')
438
+ .build();
439
+
440
+ const result = validate(conditionalSchema, 'user@example.com');
441
+ console.log(result.valid);
442
+ ```
443
+
444
+ ---
445
+
446
+ ### .elseIf(condition)
447
+
448
+ 添加 else-if 分支。
449
+
450
+ **参数**:
451
+ - `condition` {Function} - 条件函数
452
+
453
+ **返回**: `this` - 支持链式调用
454
+
455
+ **示例**:
456
+ ```javascript
457
+ dsl.if((data) => data.userType === 'admin')
458
+ .then('email!')
459
+ .elseIf((data) => data.userType === 'vip')
460
+ .then('email')
461
+ .else(null)
462
+ ```
463
+
464
+ **注意**: 必须在 `.if()` 之后调用
465
+
466
+ ---
467
+
468
+ ### .message(msg)
469
+
470
+ 设置错误消息(支持多语言 key)。
471
+
472
+ > **v1.1.1+** 支持为 `.and()` 和 `.or()` 条件设置独立消息
473
+
474
+ **参数**:
475
+ - `msg` {string} - 错误消息或多语言 key
476
+
477
+ **返回**: `this` - 支持链式调用
478
+
479
+ **行为**: 条件为 true 时自动抛出此错误(无需 `.throwError()`)
480
+
481
+ **基础示例**:
482
+ ```javascript
483
+ dsl.if((data) => data.age < 18)
484
+ .message('未成年用户不能注册')
485
+
486
+ // 支持多语言 key
487
+ dsl.if((data) => data.age < 18)
488
+ .message('error.underage')
489
+ ```
490
+
491
+ **v1.1.1+ 为 .and() 设置独立消息**:
492
+ ```javascript
493
+ // ✅ 每个条件都有自己的错误消息
494
+ dsl.if((data) => !data)
495
+ .message('账户不存在')
496
+ .and((data) => data.balance < 100)
497
+ .message('余额不足')
498
+ .assert(account);
499
+ ```
500
+
501
+ **v1.1.1+ 为 .or() 设置独立消息**:
502
+ ```javascript
503
+ // ✅ OR 条件也支持独立消息
504
+ dsl.if(d => d.age < 18)
505
+ .message('未成年')
506
+ .or(d => d.isBlocked)
507
+ .message('已封禁')
508
+ .assert(data);
509
+ ```
510
+
511
+ **链式检查模式说明** (v1.1.1+):
512
+
513
+ 当满足以下条件时,自动启用链式检查模式:
514
+ 1. 使用 `.message()` 模式(不是 `.then()`/`.else()`)
515
+ 2. root 条件有 `.message()`
516
+ 3. 有 `.and()` 条件
517
+ 4. 没有 `.or()` 条件
518
+
519
+ ```javascript
520
+ // ✅ 启用链式检查(纯 AND 场景)
521
+ dsl.if(d => !d).message('A').and(d => d < 100).message('B')
522
+
523
+ // ❌ 不启用(有 .or())
524
+ dsl.if(d => !d).message('A').and(d => d < 100).or(d => d > 200).message('B')
525
+
526
+ // ❌ 不启用(使用 .then()/.else())
527
+ dsl.if(d => d.age >= 18).and(d => d.role === 'admin').then('email!')
528
+ ```
529
+
530
+ ---
531
+
532
+ ### .then(schema)
533
+
534
+ 设置满足条件时的 Schema。
535
+
536
+ **参数**:
537
+ - `schema` {string|DslBuilder|JSONSchema} - DSL 字符串或 Schema 对象
538
+
539
+ **返回**: `this` - 支持链式调用
540
+
541
+ **示例**:
542
+ ```javascript
543
+ // DSL 字符串
544
+ dsl.if((data) => data.userType === 'admin')
545
+ .then('email!')
546
+
547
+ // DslBuilder 实例
548
+ dsl.if((data) => data.userType === 'admin')
549
+ .then(dsl('email!').label('管理员邮箱'))
550
+
551
+ // JSON Schema 对象
552
+ dsl.if((data) => data.userType === 'admin')
553
+ .then({ type: 'string', format: 'email' })
554
+ ```
555
+
556
+ ---
557
+
558
+ ### .else(schema)
559
+
560
+ 设置默认 Schema(所有条件都不满足时)。
561
+
562
+ **参数**:
563
+ - `schema` {string|DslBuilder|JSONSchema|null} - DSL 字符串、Schema 对象或 null
564
+
565
+ **返回**: `this` - 支持链式调用
566
+
567
+ **特性**: 可选,不写 else 就不验证
568
+
569
+ **示例**:
570
+ ```javascript
571
+ // 显式指定 else
572
+ dsl.if((data) => data.userType === 'admin')
573
+ .then('email!')
574
+ .else('email')
575
+
576
+ // else 为 null(显式跳过验证)
577
+ dsl.if((data) => data.userType === 'admin')
578
+ .then('email!')
579
+ .else(null)
580
+
581
+ // 不写 else(隐式跳过验证)
582
+ dsl.if((data) => data.userType === 'admin')
583
+ .then('email!')
584
+ ```
585
+
586
+ ---
587
+
588
+ ### .validate(data, options)
589
+
590
+ 快捷验证方法 - 返回完整验证结果。
591
+
592
+ **参数**:
593
+ - `data` {*} - 待验证的数据(任意类型)
594
+ - `options` {Object} - 验证选项(可选)
595
+ - `locale` {string} - 语言环境(如 'zh-CN', 'en-US')
596
+ - `messages` {Object} - 自定义错误消息
597
+
598
+ **返回**: `Object` - 验证结果 `{ valid, errors, data }`
599
+
600
+ **特性**: 一行代码完成验证,无需外部 `validate()` 函数
601
+
602
+ **示例**:
603
+ ```javascript
604
+ // 一行代码验证
605
+ const result = dsl.if(d => d.age < 18)
606
+ .message('未成年用户不能注册')
607
+ .validate({ age: 16 });
608
+ // => { valid: false, errors: [...], data }
609
+
610
+ // 复用验证器
611
+ const ageValidator = dsl.if(d => d.age < 18).message('未成年');
612
+ const r1 = ageValidator.validate({ age: 16 }); // false
613
+ const r2 = ageValidator.validate({ age: 20 }); // true
614
+
615
+ // 支持验证选项
616
+ const result = dsl.if(d => d.age < 18)
617
+ .message('conditional.underAge')
618
+ .validate({ age: 16 }, { locale: 'zh-CN' });
619
+
620
+ // 验证非对象类型
621
+ const result = dsl.if(d => d.includes('@'))
622
+ .then('email!')
623
+ .validate('test@example.com');
624
+ ```
625
+
626
+ ---
627
+
628
+ ### .validateAsync(data, options)
629
+
630
+ 异步验证方法 - 失败自动抛出异常。
631
+
632
+ **参数**:
633
+ - `data` {*} - 待验证的数据
634
+ - `options` {Object} - 验证选项(可选)
635
+
636
+ **返回**: `Promise<*>` - 验证通过返回数据,失败抛出异常
637
+
638
+ **抛出**: `ValidationError` - 验证失败抛出异常
639
+
640
+ **特性**: 适合 async/await 场景,失败自动抛错
641
+
642
+ **示例**:
643
+ ```javascript
644
+ // 异步验证,失败自动抛错
645
+ try {
646
+ const data = await dsl.if(d => d.age < 18)
647
+ .message('未成年用户不能注册')
648
+ .validateAsync({ age: 16 });
649
+ } catch (error) {
650
+ console.log(error.message); // "未成年用户不能注册"
651
+ console.log(error.errors); // 详细错误信息
652
+ }
653
+
654
+ // Express 中间件
655
+ app.post('/register', async (req, res, next) => {
656
+ try {
657
+ await dsl.if(d => d.age < 18)
658
+ .message('未成年用户不能注册')
659
+ .validateAsync(req.body);
660
+
661
+ // 验证通过,继续处理...
662
+ const user = await createUser(req.body);
663
+ res.json(user);
664
+ } catch (error) {
665
+ next(error);
666
+ }
667
+ });
668
+
669
+ // 复用验证器
670
+ const ageValidator = dsl.if(d => d.age < 18).message('未成年');
671
+
672
+ try {
673
+ await ageValidator.validateAsync({ age: 16 });
674
+ } catch (error) {
675
+ // 处理错误
676
+ }
677
+ ```
678
+
679
+ ---
680
+
681
+ ### .assert(data, options)
682
+
683
+ 断言方法 - 同步验证,失败直接抛错。
684
+
685
+ **参数**:
686
+ - `data` {*} - 待验证的数据
687
+ - `options` {Object} - 验证选项(可选)
688
+
689
+ **返回**: `*` - 验证通过返回数据
690
+
691
+ **抛出**: `ValidationError` - 验证失败时直接抛出 `ValidationError`
692
+
693
+ **特性**: 同步版本的断言验证,适合快速失败场景
694
+
695
+ **示例**:
696
+ ```javascript
697
+ // 断言验证,失败直接抛错
698
+ try {
699
+ dsl.if(d => d.age < 18)
700
+ .message('未成年用户不能注册')
701
+ .assert({ age: 16 });
702
+ } catch (error) {
703
+ console.log(error.message); // "未成年用户不能注册"
704
+ }
705
+
706
+ // 函数中快速断言
707
+ function registerUser(userData) {
708
+ // 断言验证
709
+ dsl.if(d => d.age < 18)
710
+ .message('未成年用户不能注册')
711
+ .assert(userData);
712
+
713
+ dsl.if(d => !d.email)
714
+ .message('邮箱不能为空')
715
+ .assert(userData);
716
+
717
+ // 验证通过,继续处理...
718
+ return createUser(userData);
719
+ }
720
+
721
+ // 链式断言
722
+ function validateAndCreate(data) {
723
+ dsl.if(d => d.age < 18).message('未成年').assert(data);
724
+ dsl.if(d => !d.email).message('邮箱必填').assert(data);
725
+ dsl.if(d => !d.username).message('用户名必填').assert(data);
726
+
727
+ return createUser(data);
728
+ }
729
+ ```
730
+
731
+ ---
732
+
733
+ ### .check(data)
734
+
735
+ 快捷检查方法 - 只返回 boolean。
736
+
737
+ **参数**:
738
+ - `data` {*} - 待验证的数据
739
+
740
+ **返回**: `boolean` - 验证是否通过
741
+
742
+ **特性**: 比 `.validate()` 更简洁,适合只需要判断真假的场景
743
+
744
+ **示例**:
745
+ ```javascript
746
+ // 快速判断
747
+ const isValid = dsl.if(d => d.age < 18)
748
+ .message('未成年')
749
+ .check({ age: 16 });
750
+ // => false
751
+
752
+ // 断言场景
753
+ if (!validator.check(userData)) {
754
+ console.log('验证失败');
755
+ }
756
+
757
+ // 循环验证
758
+ const users = [{ age: 16 }, { age: 20 }, { age: 17 }];
759
+ const adults = users.filter(u =>
760
+ !dsl.if(d => d.age < 18).message('未成年').check(u)
761
+ );
762
+ ```
763
+
764
+ ---
765
+
766
+ ## 使用场景
767
+
768
+ ### 场景1:用户注册 - 快捷验证
769
+
770
+ 使用 `.validate()` 方法快速验证用户注册数据。
771
+
772
+ ```javascript
773
+ // 创建可复用的验证器
774
+ const validators = {
775
+ age: dsl.if(d => d.age < 18).message('未成年用户不能注册'),
776
+ email: dsl.if(d => d.userType === 'admin')
777
+ .message('管理员必须提供邮箱')
778
+ };
779
+
780
+ // 快速验证(一行代码)
781
+ function registerUser(userData) {
782
+ // 验证年龄
783
+ const ageResult = validators.age.validate(userData);
784
+ if (!ageResult.valid) {
785
+ return { error: ageResult.errors[0].message };
786
+ }
787
+
788
+ // 验证邮箱
789
+ const emailResult = validators.email.validate(userData);
790
+ if (!emailResult.valid) {
791
+ return { error: emailResult.errors[0].message };
792
+ }
793
+
794
+ return { success: true };
795
+ }
796
+
797
+ // 使用
798
+ registerUser({ username: 'test', age: 16 });
799
+ // => { error: '未成年用户不能注册' }
800
+ ```
801
+
802
+ ### 场景2:批量数据验证 - 使用 .check()
803
+
804
+ 使用 `.check()` 方法快速过滤符合条件的数据。
805
+
806
+ ```javascript
807
+ const users = [
808
+ { name: '张三', age: 16 },
809
+ { name: '李四', age: 20 },
810
+ { name: '王五', age: 17 },
811
+ { name: '赵六', age: 25 }
812
+ ];
813
+
814
+ // 创建验证器
815
+ const canRegister = dsl.if(d => d.age < 18)
816
+ .message('未成年');
817
+
818
+ // ✅ 使用 .check() 过滤
819
+ const validUsers = users.filter(u => !canRegister.check(u));
820
+ // => [{ name: '李四', age: 20 }, { name: '赵六', age: 25 }]
821
+
822
+ // ✅ 使用 .check() 统计
823
+ const minorCount = users.filter(u => canRegister.check(u)).length;
824
+ console.log(`未成年用户: ${minorCount} 人`);
825
+ // => "未成年用户: 2 人"
826
+ ```
827
+
828
+ ### 场景3:表单实时验证
829
+
830
+ ```javascript
831
+ // 前端表单验证
832
+ const formValidators = {
833
+ username: dsl.if(d => d.length < 3)
834
+ .message('用户名至少3个字符'),
835
+
836
+ password: dsl.if(d => d.length < 8)
837
+ .message('密码至少8个字符')
838
+ };
839
+
840
+ // 实时验证(输入时)
841
+ function onUsernameChange(value) {
842
+ const isValid = formValidators.username.check(value);
843
+ if (!isValid) {
844
+ showError('用户名至少3个字符');
845
+ } else {
846
+ clearError();
847
+ }
848
+ }
849
+
850
+ // 提交验证
851
+ function onSubmit(formData) {
852
+ const usernameResult = formValidators.username.validate(formData.username);
853
+ const passwordResult = formValidators.password.validate(formData.password);
854
+
855
+ if (!usernameResult.valid) {
856
+ return alert(usernameResult.errors[0].message);
857
+ }
858
+
859
+ if (!passwordResult.valid) {
860
+ return alert(passwordResult.errors[0].message);
861
+ }
862
+
863
+ // 提交表单...
864
+ }
865
+ ```
866
+
867
+ ### 场景4:用户权限检查
868
+
869
+ ```javascript
870
+ // 权限验证器
871
+ const hasPermission = dsl.if(d => d.role === 'admin')
872
+ .or(d => d.role === 'moderator')
873
+ .message('权限不足');
874
+
875
+ // 中间件
876
+ function checkPermission(req, res, next) {
877
+ if (!hasPermission.check(req.user)) {
878
+ return res.status(403).json({ error: '权限不足' });
879
+ }
880
+ next();
881
+ }
882
+
883
+ // 路由
884
+ app.delete('/users/:id', checkPermission, deleteUser);
885
+ ```
886
+
887
+ ### 场景5:根据年龄和用户类型验证不同字段(传统方式对比)
888
+
889
+ ```javascript
890
+ // 传统方式(需要 validate 函数)
891
+ const schema = dsl({
892
+ username: 'string:3-32!',
893
+ age: 'number:1-120!',
894
+ userType: 'enum:admin|vip|user!',
895
+
896
+ // 未成年禁止注册
897
+ ageCheck: dsl.if((data) => data.age < 18)
898
+ .message('未成年用户不能注册'),
899
+
900
+ // 管理员必须有邮箱
901
+ email: dsl.if((data) => data.userType === 'admin')
902
+ .then('email!')
903
+ .else('email'),
904
+
905
+ // VIP用户必须有手机号
906
+ phone: dsl.if((data) => data.userType === 'vip')
907
+ .then('string:11!')
908
+ .else(null),
909
+
910
+ // 管理员和VIP可以设置昵称
911
+ nickname: dsl.if((data) => data.userType === 'admin')
912
+ .or((data) => data.userType === 'vip')
913
+ .then('string:2-20')
914
+ .else(null)
915
+ });
916
+
917
+ // 测试
918
+ validate(schema, {
919
+ username: 'admin1',
920
+ age: 25,
921
+ userType: 'admin',
922
+ email: 'admin@example.com'
923
+ });
924
+ // => { valid: true }
925
+ ```
926
+
927
+ ### 场景2:商品发布
928
+
929
+ 根据商品类型验证不同字段。
930
+
931
+ ```javascript
932
+ const schema = dsl({
933
+ title: 'string:1-100!',
934
+ price: 'number:0-!',
935
+ type: 'enum:physical|digital|service!',
936
+
937
+ // 实体商品需要重量和尺寸
938
+ weight: dsl.if((data) => data.type === 'physical')
939
+ .then('number:0-!')
940
+ .else(null),
941
+
942
+ dimensions: dsl.if((data) => data.type === 'physical')
943
+ .then('string!')
944
+ .else(null),
945
+
946
+ // 数字商品需要下载链接
947
+ downloadUrl: dsl.if((data) => data.type === 'digital')
948
+ .then('url!')
949
+ .else(null),
950
+
951
+ // 服务类需要服务时长
952
+ duration: dsl.if((data) => data.type === 'service')
953
+ .then('number:1-!')
954
+ .else(null)
955
+ });
956
+
957
+ // 实体商品
958
+ validate(schema, {
959
+ title: '笔记本电脑',
960
+ price: 5999,
961
+ type: 'physical',
962
+ weight: 1.5,
963
+ dimensions: '30x20x2cm'
964
+ });
965
+ // => { valid: true }
966
+
967
+ // 数字商品
968
+ validate(schema, {
969
+ title: '电子书',
970
+ price: 29.9,
971
+ type: 'digital',
972
+ downloadUrl: 'https://example.com/download'
973
+ });
974
+ // => { valid: true }
975
+ ```
976
+
977
+ ### 场景3:权限控制
978
+
979
+ 根据用户角色和状态控制访问。
980
+
981
+ ```javascript
982
+ const schema = dsl({
983
+ userId: 'string!',
984
+ role: 'enum:admin|moderator|user!',
985
+ status: 'enum:active|suspended|banned!',
986
+
987
+ // 被封禁用户禁止操作
988
+ accessCheck: dsl.if((data) => data.status === 'banned')
989
+ .message('您的账号已被封禁'),
990
+
991
+ // 暂停用户只能查看
992
+ operationType: dsl.if((data) => data.status === 'suspended')
993
+ .then('enum:view!')
994
+ .else('enum:view|edit|delete!'),
995
+
996
+ // 管理员可以访问所有资源
997
+ resourceIds: dsl.if((data) => data.role === 'admin')
998
+ .then('array<string>') // 可选
999
+ .else('array<string>!') // 必填
1000
+ });
1001
+ ```
1002
+
1003
+ ---
1004
+
1005
+ ## 最佳实践
1006
+
1007
+ ### 1. 条件函数保持简单
1008
+
1009
+ ❌ **不推荐**:
1010
+ ```javascript
1011
+ dsl.if((data) => {
1012
+ const user = getUserFromDB(data.userId); // 同步数据库查询
1013
+ return user.level > 5;
1014
+ })
1015
+ ```
1016
+
1017
+ ✅ **推荐**:
1018
+ ```javascript
1019
+ dsl.if((data) => data.userLevel > 5)
1020
+ ```
1021
+
1022
+ **原因**: 条件函数应该只读取数据对象,不应该有副作用或执行耗时操作。
1023
+
1024
+ ---
1025
+
1026
+ ### 2. 使用有意义的字段名
1027
+
1028
+ ❌ **不推荐**:
1029
+ ```javascript
1030
+ const schema = dsl({
1031
+ field1: 'string!',
1032
+ check1: dsl.if((data) => data.field1 === 'admin')
1033
+ .message('Error')
1034
+ });
1035
+ ```
1036
+
1037
+ ✅ **推荐**:
1038
+ ```javascript
1039
+ const schema = dsl({
1040
+ userType: 'string!',
1041
+ ageVerification: dsl.if((data) => data.age < 18)
1042
+ .message('未成年用户不能注册')
1043
+ });
1044
+ ```
1045
+
1046
+ ---
1047
+
1048
+ ### 3. 合理使用 else
1049
+
1050
+ 当条件不满足时需要不同的验证规则,使用 `.else()`:
1051
+
1052
+ ```javascript
1053
+ dsl.if((data) => data.userType === 'admin')
1054
+ .then('email!')
1055
+ .else('email') // 不同的验证规则
1056
+ ```
1057
+
1058
+ 当条件不满足时不需要验证,省略 `.else()`:
1059
+
1060
+ ```javascript
1061
+ dsl.if((data) => data.userType === 'vip')
1062
+ .then('string:6!')
1063
+ // 不写 else,非 vip 用户不验证
1064
+ ```
1065
+
1066
+ ---
1067
+
1068
+ ### 4. 多条件组合优先使用函数内部逻辑
1069
+
1070
+ 简单条件可以直接在函数内部组合:
1071
+
1072
+ ```javascript
1073
+ // ✅ 推荐(简洁)
1074
+ dsl.if((data) => data.age >= 18 && data.userType === 'admin')
1075
+ .then('email!')
1076
+
1077
+ // ⚠️ 可用但稍繁琐
1078
+ dsl.if((data) => data.age >= 18)
1079
+ .and((data) => data.userType === 'admin')
1080
+ .then('email!')
1081
+ ```
1082
+
1083
+ 复杂逻辑或需要可维护性时使用 `.and()` / `.or()`:
1084
+
1085
+ ```javascript
1086
+ // ✅ 推荐(可读性强)
1087
+ dsl.if((data) => data.age >= 18)
1088
+ .and((data) => data.userType === 'admin')
1089
+ .and((data) => data.verified)
1090
+ .or((data) => data.isSuperUser)
1091
+ .then('email!')
1092
+ ```
1093
+
1094
+ ---
1095
+
1096
+ ### 5. 错误消息清晰明确
1097
+
1098
+ ❌ **不推荐**:
1099
+ ```javascript
1100
+ dsl.if((data) => data.age < 18)
1101
+ .message('Error')
1102
+ ```
1103
+
1104
+ ✅ **推荐**:
1105
+ ```javascript
1106
+ dsl.if((data) => data.age < 18)
1107
+ .message('未成年用户不能注册')
1108
+ ```
1109
+
1110
+ ✅ **更好**(支持多语言):
1111
+ ```javascript
1112
+ dsl.if((data) => data.age < 18)
1113
+ .message('error.user.underage')
1114
+ ```
1115
+
1116
+ ---
1117
+
1118
+ ## 常见问题
1119
+
1120
+ ### Q1: 条件函数什么时候执行?
1121
+
1122
+ **A**: 在调用 `validate()` 时执行,不是在定义 Schema 时。
1123
+
1124
+ ```javascript
1125
+ const schema = dsl({
1126
+ email: dsl.if((data) => data.userType === 'admin')
1127
+ .then('email!') // ← 这里不会执行
1128
+ });
1129
+
1130
+ validate(schema, data); // ← 条件函数在这里执行
1131
+ ```
1132
+
1133
+ ---
1134
+
1135
+ ### Q2: 条件函数可以访问哪些数据?
1136
+
1137
+ **A**: 可以访问完整的数据对象。
1138
+
1139
+ ```javascript
1140
+ const schema = dsl({
1141
+ age: 'number!',
1142
+ userType: 'string!',
1143
+ status: 'string!',
1144
+ email: dsl.if((data) => {
1145
+ // 可以访问所有字段
1146
+ return data.age >= 18 && data.userType === 'admin' && data.status === 'active';
1147
+ }).then('email!')
1148
+ });
1149
+ ```
1150
+
1151
+ ---
1152
+
1153
+ ### Q3: 如何处理条件函数抛错?
1154
+
1155
+ **A**: 条件函数抛错会被捕获,视为条件不满足。
1156
+
1157
+ ```javascript
1158
+ const schema = dsl({
1159
+ obj: 'object!',
1160
+ result: dsl.if((data) => data.obj.nested.value > 10)
1161
+ .then('string!')
1162
+ .else(null)
1163
+ });
1164
+
1165
+ // data.obj.nested 不存在,访问会抛错
1166
+ validate(schema, { obj: {} });
1167
+ // => { valid: true } 条件不满足,执行 else(null)
1168
+ ```
1169
+
1170
+ **建议**: 在条件函数中做好防御性检查:
1171
+
1172
+ ```javascript
1173
+ dsl.if((data) => data.obj?.nested?.value > 10)
1174
+ .then('string!')
1175
+ ```
1176
+
1177
+ ---
1178
+
1179
+ ### Q4: 可以嵌套 dsl.if() 吗?
1180
+
1181
+ **A**: 可以,支持嵌套。
1182
+
1183
+ ```javascript
1184
+ const schema = dsl({
1185
+ userType: 'string!',
1186
+ age: 'number!',
1187
+ email: dsl.if((data) => data.userType === 'admin')
1188
+ .then(
1189
+ dsl.if((data) => data.age >= 18)
1190
+ .then('email!')
1191
+ .else('email')
1192
+ )
1193
+ .else('email')
1194
+ });
1195
+ ```
1196
+
1197
+ ---
1198
+
1199
+ ### Q5: 如何与现有的 dsl.match() 方法配合使用?
1200
+
1201
+ **A**: 可以混用,选择最适合的方法。
1202
+
1203
+ ```javascript
1204
+ const schema = dsl({
1205
+ // 静态值映射 - 使用 match
1206
+ userType: 'enum:admin|vip|user!',
1207
+ level: dsl.match('userType', {
1208
+ admin: 'enum:high!',
1209
+ vip: 'enum:medium!',
1210
+ user: 'enum:low!'
1211
+ }),
1212
+
1213
+ // 动态条件判断 - 使用 if
1214
+ email: dsl.if((data) => data.userType === 'admin' && data.level === 'high')
1215
+ .then('email!')
1216
+ .else('email')
1217
+ });
1218
+ ```
1219
+
1220
+ **选择建议**:
1221
+ - **简单值映射** → 使用 `dsl.match()`
1222
+ - **复杂条件逻辑** → 使用 `dsl.if()`
1223
+
1224
+ ---
1225
+
1226
+ ### Q6: 是否支持非对象类型(字符串、数组、数字等)?
1227
+
1228
+ **A**: 完全支持!可以直接验证任何类型的值。
1229
+
1230
+ ```javascript
1231
+ // 示例1:直接验证字符串
1232
+ const stringSchema = dsl.if((data) => typeof data === 'string' && data.includes('@'))
1233
+ .then('email!')
1234
+ .else('string:1-50');
1235
+
1236
+ validate(stringSchema, 'test@example.com'); // ✅ 作为邮箱验证
1237
+ validate(stringSchema, 'just a text'); // ✅ 作为普通字符串验证
1238
+
1239
+ // 示例2:直接验证数组
1240
+ const arraySchema = dsl.if((data) => Array.isArray(data) && data.length > 5)
1241
+ .message('数组最多5个元素');
1242
+
1243
+ validate(arraySchema, [1, 2, 3]); // ✅ 通过
1244
+ validate(arraySchema, [1, 2, 3, 4, 5, 6]); // ❌ 失败
1245
+
1246
+ // 示例3:直接验证数字
1247
+ const numberSchema = dsl.if((data) => typeof data === 'number' && data < 0)
1248
+ .message('不允许负数');
1249
+
1250
+ validate(numberSchema, 10); // ✅ 通过
1251
+ validate(numberSchema, -5); // ❌ 失败
1252
+
1253
+ // 示例4:自动识别类型(邮箱或手机号)
1254
+ const contactSchema = dsl.if((data) => typeof data === 'string' && data.includes('@'))
1255
+ .then('email!')
1256
+ .else('string:11!');
1257
+
1258
+ validate(contactSchema, 'user@example.com'); // ✅ 作为邮箱验证
1259
+ validate(contactSchema, '13800138000'); // ✅ 作为手机号验证
1260
+ ```
1261
+
1262
+ **完整示例**: 参见 `test/unit/conditional-non-object.test.ts`
1263
+
1264
+ ---
1265
+
1266
+ ### Q7: 性能如何?
1267
+
1268
+ **A**: 性能优秀,条件函数执行非常快。
1269
+
1270
+ - 条件函数是纯 JavaScript 函数,执行速度快
1271
+ - 只遍历条件链一次,找到第一个匹配的条件就停止
1272
+ - 支持缓存优化(WeakMap)
1273
+
1274
+ **性能提示**:
1275
+ - 避免在条件函数中执行耗时操作(数据库查询、API 调用)
1276
+ - 将最常见的条件放在前面(if 而不是 elseIf)
1277
+
1278
+ ---
1279
+
1280
+ ## 更新日志
1281
+
1282
+ ### v1.1.1 (2026-01-05)
1283
+
1284
+ - ✅ 新增 `ConditionalBuilder` 类
1285
+ - ✅ 新增 `dsl.if()` 链式条件 API
1286
+ - ✅ 支持 and/or 多条件组合
1287
+ - ✅ 支持 elseIf 多分支
1288
+ - ✅ message 自动抛错(无需 throwError)
1289
+ - ✅ else 可选(不写就不验证)
1290
+ - ❌ 移除无效的旧条件方法类型定义
1291
+
1292
+ ---
1293
+
1294
+ ## 相关文档
1295
+
1296
+ - [快速开始](./quick-start.md)
1297
+ - [验证指南](./validation-guide.md)
1298
+ - [API 参考](./api-reference.md)
1299
+ - [最佳实践](./best-practices.md)
1300
+
1301
+ ---
1302
+
1303
+ ## 对应示例文件
1304
+
1305
+ **示例入口**: [conditional-api.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/conditional-api.ts)
1306
+ **说明**: 同时覆盖失败谓词模式下的 `.check()` / `.assert()`,以及字段名版本 `dsl.if(field, then, else)` 和 `dsl.match()` 映射。
1307
+