schema-dsl 1.0.8 → 1.1.0
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.
- package/CHANGELOG.md +338 -3
- package/README.md +296 -17
- package/STATUS.md +74 -3
- package/docs/FEATURE-INDEX.md +1 -1
- package/docs/add-custom-locale.md +395 -0
- package/docs/best-practices.md +3 -3
- package/docs/cache-manager.md +1 -1
- package/docs/conditional-api.md +1032 -0
- package/docs/dsl-syntax.md +1 -1
- package/docs/dynamic-locale.md +76 -30
- package/docs/error-handling.md +2 -2
- package/docs/export-guide.md +2 -2
- package/docs/export-limitations.md +3 -3
- package/docs/faq.md +6 -6
- package/docs/frontend-i18n-guide.md +19 -16
- package/docs/i18n-user-guide.md +7 -9
- package/docs/i18n.md +65 -2
- package/docs/mongodb-exporter.md +3 -3
- package/docs/multi-type-support.md +12 -2
- package/docs/mysql-exporter.md +1 -1
- package/docs/plugin-system.md +4 -4
- package/docs/postgresql-exporter.md +1 -1
- package/docs/quick-start.md +4 -4
- package/docs/troubleshooting.md +2 -2
- package/docs/type-reference.md +5 -5
- package/docs/typescript-guide.md +5 -6
- package/docs/union-type-guide.md +147 -0
- package/docs/union-types.md +277 -0
- package/docs/validate-async.md +1 -1
- package/examples/array-dsl-example.js +1 -1
- package/examples/conditional-example.js +288 -0
- package/examples/conditional-non-object.js +129 -0
- package/examples/conditional-validate-example.js +321 -0
- package/examples/union-type-example.js +127 -0
- package/examples/union-types-example.js +77 -0
- package/index.d.ts +395 -12
- package/index.js +31 -4
- package/lib/adapters/DslAdapter.js +14 -5
- package/lib/core/ConditionalBuilder.js +401 -0
- package/lib/core/DslBuilder.js +113 -0
- package/lib/core/ErrorFormatter.js +81 -33
- package/lib/core/Locale.js +13 -8
- package/lib/core/Validator.js +252 -16
- package/lib/locales/en-US.js +14 -0
- package/lib/locales/es-ES.js +4 -0
- package/lib/locales/fr-FR.js +4 -0
- package/lib/locales/ja-JP.js +9 -0
- package/lib/locales/zh-CN.js +14 -0
- package/package.json +5 -2
|
@@ -0,0 +1,1032 @@
|
|
|
1
|
+
# 链式条件 API - ConditionalBuilder
|
|
2
|
+
|
|
3
|
+
> **版本**: v1.1.0
|
|
4
|
+
> **更新日期**: 2026-01-05
|
|
5
|
+
> **状态**: ✅ 稳定
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 📋 目录
|
|
10
|
+
|
|
11
|
+
- [概述](#概述)
|
|
12
|
+
- [快速开始](#快速开始)
|
|
13
|
+
- [API 参考](#api-参考)
|
|
14
|
+
- [使用场景](#使用场景)
|
|
15
|
+
- [最佳实践](#最佳实践)
|
|
16
|
+
- [常见问题](#常见问题)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 概述
|
|
21
|
+
|
|
22
|
+
`ConditionalBuilder` 提供流畅的链式条件判断 API,类似 JavaScript 的 if-else 语句,用于在验证时根据实际数据动态调整验证规则。
|
|
23
|
+
|
|
24
|
+
### 核心特性
|
|
25
|
+
|
|
26
|
+
- ✅ **链式调用** - 流畅的 API,类似 JavaScript if-else
|
|
27
|
+
- ✅ **运行时执行** - 在验证时根据实际数据判断
|
|
28
|
+
- ✅ **多条件组合** - 支持 and/or 逻辑组合
|
|
29
|
+
- ✅ **else 可选** - 不写 else 就不验证
|
|
30
|
+
- ✅ **简化设计** - message 自动抛错,无需 throwError()
|
|
31
|
+
- ✅ **完全兼容** - 不影响现有 API
|
|
32
|
+
|
|
33
|
+
### 与现有方法的区别
|
|
34
|
+
|
|
35
|
+
`dsl.if()` 提供两种使用方式,根据参数类型自动选择:
|
|
36
|
+
|
|
37
|
+
| 方式 | 参数类型 | 执行时机 | 用途 | 示例 |
|
|
38
|
+
|------|---------|---------|------|------|
|
|
39
|
+
| **方式一** | 字符串 | Schema 定义时 | 静态布尔条件 | `dsl.if('isVip', thenSchema, elseSchema)` |
|
|
40
|
+
| **方式二** | 函数 | 验证时 | 动态条件判断 | `dsl.if((data) => data.age >= 18).then(...)` |
|
|
41
|
+
|
|
42
|
+
**方式一**(字段条件):基于字段值的静态判断
|
|
43
|
+
```javascript
|
|
44
|
+
// 示例:根据 isVip 字段值选择不同的验证规则
|
|
45
|
+
dsl.if('isVip', 'number:0-50', 'number:0-10')
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**方式二**(函数条件):基于完整数据的动态判断
|
|
49
|
+
```javascript
|
|
50
|
+
// 示例:根据多个字段的组合逻辑动态选择
|
|
51
|
+
dsl.if((data) => data.age >= 18 && data.role === 'admin')
|
|
52
|
+
.then('email!')
|
|
53
|
+
.else('email')
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
此外,`dsl.match()` 适用于多值映射场景:
|
|
57
|
+
```javascript
|
|
58
|
+
// 示例:根据 type 字段值映射不同验证规则
|
|
59
|
+
dsl.match('type', {
|
|
60
|
+
email: 'email!',
|
|
61
|
+
phone: 'string:11!',
|
|
62
|
+
_default: 'string'
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 快速开始
|
|
69
|
+
|
|
70
|
+
### 基础用法
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
const { dsl, validate } = require('schema-dsl');
|
|
74
|
+
|
|
75
|
+
// 方式1:传统方式(需要 validate 函数)
|
|
76
|
+
const schema1 = dsl({
|
|
77
|
+
age: 'number!',
|
|
78
|
+
status: dsl.if((data) => data.age >= 18)
|
|
79
|
+
.message('未成年用户不能注册')
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
validate(schema1, { age: 16, status: 'active' });
|
|
83
|
+
// => { valid: false, errors: [{ message: '未成年用户不能注册' }] }
|
|
84
|
+
|
|
85
|
+
// ✅ 方式2:快捷方式(一行代码验证)
|
|
86
|
+
const result = dsl.if((data) => data.age >= 18)
|
|
87
|
+
.message('未成年用户不能注册')
|
|
88
|
+
.validate({ age: 16 });
|
|
89
|
+
// => { valid: false, errors: [{ message: '未成年用户不能注册' }] }
|
|
90
|
+
|
|
91
|
+
// ✅ 方式3:.check() 快速判断
|
|
92
|
+
const isValid = dsl.if((data) => data.age >= 18)
|
|
93
|
+
.message('未成年用户不能注册')
|
|
94
|
+
.check({ age: 16 });
|
|
95
|
+
// => false
|
|
96
|
+
|
|
97
|
+
// 2. 条件 + then/else(动态Schema)
|
|
98
|
+
const result = dsl.if((data) => data.userType === 'admin')
|
|
99
|
+
.then('email!') // 管理员必填
|
|
100
|
+
.else('email') // 普通用户可选
|
|
101
|
+
.validate({ userType: 'admin', email: 'admin@example.com' });
|
|
102
|
+
|
|
103
|
+
// 3. else 可选
|
|
104
|
+
const result = dsl.if((data) => data.userType === 'vip')
|
|
105
|
+
.then('enum:gold|silver|bronze!')
|
|
106
|
+
// 不写 else,非 vip 用户不验证
|
|
107
|
+
.validate({ userType: 'user' });
|
|
108
|
+
|
|
109
|
+
// 4. 复用验证器
|
|
110
|
+
const ageValidator = dsl.if(d => d.age < 18).message('未成年用户不能注册');
|
|
111
|
+
const r1 = ageValidator.validate({ age: 16 }); // 失败
|
|
112
|
+
const r2 = ageValidator.validate({ age: 20 }); // 通过
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 多条件组合
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
// 1. AND 条件
|
|
119
|
+
const result = dsl.if((data) => data.age >= 18)
|
|
120
|
+
.and((data) => data.userType === 'admin')
|
|
121
|
+
.message('只有成年管理员可以操作')
|
|
122
|
+
.validate({ age: 20, userType: 'user' });
|
|
123
|
+
|
|
124
|
+
// 2. OR 条件
|
|
125
|
+
const result = dsl.if((data) => data.age < 18)
|
|
126
|
+
.or((data) => data.status === 'blocked')
|
|
127
|
+
.message('不允许注册')
|
|
128
|
+
.validate({ age: 16, status: 'active' });
|
|
129
|
+
|
|
130
|
+
// 3. 复杂组合
|
|
131
|
+
const result = dsl.if((data) => data.age >= 18)
|
|
132
|
+
.and((data) => data.userType === 'admin')
|
|
133
|
+
.or((data) => data.status === 'vip')
|
|
134
|
+
.then('email!')
|
|
135
|
+
.else('email')
|
|
136
|
+
.validate(data);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### elseIf 分支
|
|
140
|
+
|
|
141
|
+
```javascript
|
|
142
|
+
const validator = dsl.if((data) => data.userType === 'admin')
|
|
143
|
+
.then('array<string>!')
|
|
144
|
+
.elseIf((data) => data.userType === 'vip')
|
|
145
|
+
.then('array<string>')
|
|
146
|
+
.elseIf((data) => data.userType === 'user')
|
|
147
|
+
.then('array')
|
|
148
|
+
.else(null); // 游客不验证
|
|
149
|
+
|
|
150
|
+
const r1 = validator.validate({ userType: 'admin', permissions: ['read', 'write'] });
|
|
151
|
+
const r2 = validator.validate({ userType: 'vip' });
|
|
152
|
+
const r3 = validator.validate({ userType: 'guest' });
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## API 参考
|
|
158
|
+
|
|
159
|
+
### dsl.if(condition)
|
|
160
|
+
|
|
161
|
+
创建链式条件构建器。
|
|
162
|
+
|
|
163
|
+
**参数**:
|
|
164
|
+
- `condition` {Function} - 条件函数,接收完整数据对象
|
|
165
|
+
- 参数: `(data: any) => boolean`
|
|
166
|
+
- 返回: `boolean` - true 表示条件满足
|
|
167
|
+
|
|
168
|
+
**返回**: `ConditionalBuilder` - 构建器实例
|
|
169
|
+
|
|
170
|
+
**示例**:
|
|
171
|
+
```javascript
|
|
172
|
+
dsl.if((data) => data.age >= 18)
|
|
173
|
+
dsl.if((data) => data.userType === 'admin')
|
|
174
|
+
dsl.if((data) => data.status === 'active' && data.verified)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
### .and(condition)
|
|
180
|
+
|
|
181
|
+
添加 AND 条件(与前一个条件组合)。
|
|
182
|
+
|
|
183
|
+
**参数**:
|
|
184
|
+
- `condition` {Function} - 条件函数
|
|
185
|
+
|
|
186
|
+
**返回**: `this` - 支持链式调用
|
|
187
|
+
|
|
188
|
+
**示例**:
|
|
189
|
+
```javascript
|
|
190
|
+
dsl.if((data) => data.age >= 18)
|
|
191
|
+
.and((data) => data.userType === 'admin')
|
|
192
|
+
.then('email!')
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**逻辑**: `(condition1 AND condition2)`
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
### .or(condition)
|
|
200
|
+
|
|
201
|
+
添加 OR 条件(与前一个条件组合)。
|
|
202
|
+
|
|
203
|
+
**参数**:
|
|
204
|
+
- `condition` {Function} - 条件函数
|
|
205
|
+
|
|
206
|
+
**返回**: `this` - 支持链式调用
|
|
207
|
+
|
|
208
|
+
**示例**:
|
|
209
|
+
```javascript
|
|
210
|
+
dsl.if((data) => data.age < 18)
|
|
211
|
+
.or((data) => data.status === 'blocked')
|
|
212
|
+
.message('不允许注册')
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**逻辑**: `(condition1 OR condition2)`
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
### .elseIf(condition)
|
|
220
|
+
|
|
221
|
+
添加 else-if 分支。
|
|
222
|
+
|
|
223
|
+
**参数**:
|
|
224
|
+
- `condition` {Function} - 条件函数
|
|
225
|
+
|
|
226
|
+
**返回**: `this` - 支持链式调用
|
|
227
|
+
|
|
228
|
+
**示例**:
|
|
229
|
+
```javascript
|
|
230
|
+
dsl.if((data) => data.userType === 'admin')
|
|
231
|
+
.then('email!')
|
|
232
|
+
.elseIf((data) => data.userType === 'vip')
|
|
233
|
+
.then('email')
|
|
234
|
+
.else(null)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**注意**: 必须在 `.if()` 之后调用
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
### .message(msg)
|
|
242
|
+
|
|
243
|
+
设置错误消息(支持多语言 key)。
|
|
244
|
+
|
|
245
|
+
**参数**:
|
|
246
|
+
- `msg` {string} - 错误消息或多语言 key
|
|
247
|
+
|
|
248
|
+
**返回**: `this` - 支持链式调用
|
|
249
|
+
|
|
250
|
+
**行为**: 不满足条件时自动抛出此错误(无需 `.throwError()`)
|
|
251
|
+
|
|
252
|
+
**示例**:
|
|
253
|
+
```javascript
|
|
254
|
+
dsl.if((data) => data.age >= 18)
|
|
255
|
+
.message('未成年用户不能注册')
|
|
256
|
+
|
|
257
|
+
// 支持多语言 key
|
|
258
|
+
dsl.if((data) => data.age >= 18)
|
|
259
|
+
.message('error.underage')
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
### .then(schema)
|
|
265
|
+
|
|
266
|
+
设置满足条件时的 Schema。
|
|
267
|
+
|
|
268
|
+
**参数**:
|
|
269
|
+
- `schema` {string|DslBuilder|JSONSchema} - DSL 字符串或 Schema 对象
|
|
270
|
+
|
|
271
|
+
**返回**: `this` - 支持链式调用
|
|
272
|
+
|
|
273
|
+
**示例**:
|
|
274
|
+
```javascript
|
|
275
|
+
// DSL 字符串
|
|
276
|
+
dsl.if((data) => data.userType === 'admin')
|
|
277
|
+
.then('email!')
|
|
278
|
+
|
|
279
|
+
// DslBuilder 实例
|
|
280
|
+
dsl.if((data) => data.userType === 'admin')
|
|
281
|
+
.then(dsl('email!').label('管理员邮箱'))
|
|
282
|
+
|
|
283
|
+
// JSON Schema 对象
|
|
284
|
+
dsl.if((data) => data.userType === 'admin')
|
|
285
|
+
.then({ type: 'string', format: 'email' })
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
### .else(schema)
|
|
291
|
+
|
|
292
|
+
设置默认 Schema(所有条件都不满足时)。
|
|
293
|
+
|
|
294
|
+
**参数**:
|
|
295
|
+
- `schema` {string|DslBuilder|JSONSchema|null} - DSL 字符串、Schema 对象或 null
|
|
296
|
+
|
|
297
|
+
**返回**: `this` - 支持链式调用
|
|
298
|
+
|
|
299
|
+
**特性**: 可选,不写 else 就不验证
|
|
300
|
+
|
|
301
|
+
**示例**:
|
|
302
|
+
```javascript
|
|
303
|
+
// 显式指定 else
|
|
304
|
+
dsl.if((data) => data.userType === 'admin')
|
|
305
|
+
.then('email!')
|
|
306
|
+
.else('email')
|
|
307
|
+
|
|
308
|
+
// else 为 null(显式跳过验证)
|
|
309
|
+
dsl.if((data) => data.userType === 'admin')
|
|
310
|
+
.then('email!')
|
|
311
|
+
.else(null)
|
|
312
|
+
|
|
313
|
+
// 不写 else(隐式跳过验证)
|
|
314
|
+
dsl.if((data) => data.userType === 'admin')
|
|
315
|
+
.then('email!')
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
### .validate(data, options)
|
|
321
|
+
|
|
322
|
+
快捷验证方法 - 返回完整验证结果。
|
|
323
|
+
|
|
324
|
+
**参数**:
|
|
325
|
+
- `data` {*} - 待验证的数据(任意类型)
|
|
326
|
+
- `options` {Object} - 验证选项(可选)
|
|
327
|
+
- `locale` {string} - 语言环境(如 'zh-CN', 'en-US')
|
|
328
|
+
- `messages` {Object} - 自定义错误消息
|
|
329
|
+
|
|
330
|
+
**返回**: `Object` - 验证结果 `{ valid, errors, data }`
|
|
331
|
+
|
|
332
|
+
**特性**: 一行代码完成验证,无需外部 `validate()` 函数
|
|
333
|
+
|
|
334
|
+
**示例**:
|
|
335
|
+
```javascript
|
|
336
|
+
// 一行代码验证
|
|
337
|
+
const result = dsl.if(d => d.age < 18)
|
|
338
|
+
.message('未成年用户不能注册')
|
|
339
|
+
.validate({ age: 16 });
|
|
340
|
+
// => { valid: false, errors: [...], data }
|
|
341
|
+
|
|
342
|
+
// 复用验证器
|
|
343
|
+
const ageValidator = dsl.if(d => d.age < 18).message('未成年');
|
|
344
|
+
const r1 = ageValidator.validate({ age: 16 }); // false
|
|
345
|
+
const r2 = ageValidator.validate({ age: 20 }); // true
|
|
346
|
+
|
|
347
|
+
// 支持验证选项
|
|
348
|
+
const result = dsl.if(d => d.age < 18)
|
|
349
|
+
.message('conditional.underAge')
|
|
350
|
+
.validate({ age: 16 }, { locale: 'zh-CN' });
|
|
351
|
+
|
|
352
|
+
// 验证非对象类型
|
|
353
|
+
const result = dsl.if(d => d.includes('@'))
|
|
354
|
+
.then('email!')
|
|
355
|
+
.validate('test@example.com');
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
### .validateAsync(data, options)
|
|
361
|
+
|
|
362
|
+
异步验证方法 - 失败自动抛出异常。
|
|
363
|
+
|
|
364
|
+
**参数**:
|
|
365
|
+
- `data` {*} - 待验证的数据
|
|
366
|
+
- `options` {Object} - 验证选项(可选)
|
|
367
|
+
|
|
368
|
+
**返回**: `Promise<*>` - 验证通过返回数据,失败抛出异常
|
|
369
|
+
|
|
370
|
+
**抛出**: `ValidationError` - 验证失败抛出异常
|
|
371
|
+
|
|
372
|
+
**特性**: 适合 async/await 场景,失败自动抛错
|
|
373
|
+
|
|
374
|
+
**示例**:
|
|
375
|
+
```javascript
|
|
376
|
+
// 异步验证,失败自动抛错
|
|
377
|
+
try {
|
|
378
|
+
const data = await dsl.if(d => d.age < 18)
|
|
379
|
+
.message('未成年用户不能注册')
|
|
380
|
+
.validateAsync({ age: 16 });
|
|
381
|
+
} catch (error) {
|
|
382
|
+
console.log(error.message); // "未成年用户不能注册"
|
|
383
|
+
console.log(error.errors); // 详细错误信息
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Express 中间件
|
|
387
|
+
app.post('/register', async (req, res, next) => {
|
|
388
|
+
try {
|
|
389
|
+
await dsl.if(d => d.age < 18)
|
|
390
|
+
.message('未成年用户不能注册')
|
|
391
|
+
.validateAsync(req.body);
|
|
392
|
+
|
|
393
|
+
// 验证通过,继续处理...
|
|
394
|
+
const user = await createUser(req.body);
|
|
395
|
+
res.json(user);
|
|
396
|
+
} catch (error) {
|
|
397
|
+
next(error);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// 复用验证器
|
|
402
|
+
const ageValidator = dsl.if(d => d.age < 18).message('未成年');
|
|
403
|
+
|
|
404
|
+
try {
|
|
405
|
+
await ageValidator.validateAsync({ age: 16 });
|
|
406
|
+
} catch (error) {
|
|
407
|
+
// 处理错误
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
### .assert(data, options)
|
|
414
|
+
|
|
415
|
+
断言方法 - 同步验证,失败直接抛错。
|
|
416
|
+
|
|
417
|
+
**参数**:
|
|
418
|
+
- `data` {*} - 待验证的数据
|
|
419
|
+
- `options` {Object} - 验证选项(可选)
|
|
420
|
+
|
|
421
|
+
**返回**: `*` - 验证通过返回数据
|
|
422
|
+
|
|
423
|
+
**抛出**: `Error` - 验证失败抛出错误(name: 'ValidationError')
|
|
424
|
+
|
|
425
|
+
**特性**: 同步版本的断言验证,适合快速失败场景
|
|
426
|
+
|
|
427
|
+
**示例**:
|
|
428
|
+
```javascript
|
|
429
|
+
// 断言验证,失败直接抛错
|
|
430
|
+
try {
|
|
431
|
+
dsl.if(d => d.age < 18)
|
|
432
|
+
.message('未成年用户不能注册')
|
|
433
|
+
.assert({ age: 16 });
|
|
434
|
+
} catch (error) {
|
|
435
|
+
console.log(error.message); // "未成年用户不能注册"
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// 函数中快速断言
|
|
439
|
+
function registerUser(userData) {
|
|
440
|
+
// 断言验证
|
|
441
|
+
dsl.if(d => d.age < 18)
|
|
442
|
+
.message('未成年用户不能注册')
|
|
443
|
+
.assert(userData);
|
|
444
|
+
|
|
445
|
+
dsl.if(d => !d.email)
|
|
446
|
+
.message('邮箱不能为空')
|
|
447
|
+
.assert(userData);
|
|
448
|
+
|
|
449
|
+
// 验证通过,继续处理...
|
|
450
|
+
return createUser(userData);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// 链式断言
|
|
454
|
+
function validateAndCreate(data) {
|
|
455
|
+
dsl.if(d => d.age < 18).message('未成年').assert(data);
|
|
456
|
+
dsl.if(d => !d.email).message('邮箱必填').assert(data);
|
|
457
|
+
dsl.if(d => !d.username).message('用户名必填').assert(data);
|
|
458
|
+
|
|
459
|
+
return createUser(data);
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
### .check(data)
|
|
466
|
+
|
|
467
|
+
快捷检查方法 - 只返回 boolean。
|
|
468
|
+
|
|
469
|
+
**参数**:
|
|
470
|
+
- `data` {*} - 待验证的数据
|
|
471
|
+
|
|
472
|
+
**返回**: `boolean` - 验证是否通过
|
|
473
|
+
|
|
474
|
+
**特性**: 比 `.validate()` 更简洁,适合只需要判断真假的场景
|
|
475
|
+
|
|
476
|
+
**示例**:
|
|
477
|
+
```javascript
|
|
478
|
+
// 快速判断
|
|
479
|
+
const isValid = dsl.if(d => d.age < 18)
|
|
480
|
+
.message('未成年')
|
|
481
|
+
.check({ age: 16 });
|
|
482
|
+
// => false
|
|
483
|
+
|
|
484
|
+
// 断言场景
|
|
485
|
+
if (!validator.check(userData)) {
|
|
486
|
+
console.log('验证失败');
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// 循环验证
|
|
490
|
+
const users = [{ age: 16 }, { age: 20 }, { age: 17 }];
|
|
491
|
+
const adults = users.filter(u =>
|
|
492
|
+
!dsl.if(d => d.age < 18).message('未成年').check(u)
|
|
493
|
+
);
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
498
|
+
## 使用场景
|
|
499
|
+
|
|
500
|
+
### 场景1:用户注册 - 快捷验证
|
|
501
|
+
|
|
502
|
+
使用 `.validate()` 方法快速验证用户注册数据。
|
|
503
|
+
|
|
504
|
+
```javascript
|
|
505
|
+
// 创建可复用的验证器
|
|
506
|
+
const validators = {
|
|
507
|
+
age: dsl.if(d => d.age < 18).message('未成年用户不能注册'),
|
|
508
|
+
email: dsl.if(d => d.userType === 'admin')
|
|
509
|
+
.message('管理员必须提供邮箱')
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
// 快速验证(一行代码)
|
|
513
|
+
function registerUser(userData) {
|
|
514
|
+
// 验证年龄
|
|
515
|
+
const ageResult = validators.age.validate(userData);
|
|
516
|
+
if (!ageResult.valid) {
|
|
517
|
+
return { error: ageResult.errors[0].message };
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// 验证邮箱
|
|
521
|
+
const emailResult = validators.email.validate(userData);
|
|
522
|
+
if (!emailResult.valid) {
|
|
523
|
+
return { error: emailResult.errors[0].message };
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
return { success: true };
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// 使用
|
|
530
|
+
registerUser({ username: 'test', age: 16 });
|
|
531
|
+
// => { error: '未成年用户不能注册' }
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### 场景2:批量数据验证 - 使用 .check()
|
|
535
|
+
|
|
536
|
+
使用 `.check()` 方法快速过滤符合条件的数据。
|
|
537
|
+
|
|
538
|
+
```javascript
|
|
539
|
+
const users = [
|
|
540
|
+
{ name: '张三', age: 16 },
|
|
541
|
+
{ name: '李四', age: 20 },
|
|
542
|
+
{ name: '王五', age: 17 },
|
|
543
|
+
{ name: '赵六', age: 25 }
|
|
544
|
+
];
|
|
545
|
+
|
|
546
|
+
// 创建验证器
|
|
547
|
+
const canRegister = dsl.if(d => d.age < 18)
|
|
548
|
+
.message('未成年');
|
|
549
|
+
|
|
550
|
+
// ✅ 使用 .check() 过滤
|
|
551
|
+
const validUsers = users.filter(u => !canRegister.check(u));
|
|
552
|
+
// => [{ name: '李四', age: 20 }, { name: '赵六', age: 25 }]
|
|
553
|
+
|
|
554
|
+
// ✅ 使用 .check() 统计
|
|
555
|
+
const minorCount = users.filter(u => canRegister.check(u)).length;
|
|
556
|
+
console.log(`未成年用户: ${minorCount} 人`);
|
|
557
|
+
// => "未成年用户: 2 人"
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### 场景3:表单实时验证
|
|
561
|
+
|
|
562
|
+
```javascript
|
|
563
|
+
// 前端表单验证
|
|
564
|
+
const formValidators = {
|
|
565
|
+
username: dsl.if(d => d.length < 3)
|
|
566
|
+
.message('用户名至少3个字符'),
|
|
567
|
+
|
|
568
|
+
password: dsl.if(d => d.length < 8)
|
|
569
|
+
.message('密码至少8个字符')
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
// 实时验证(输入时)
|
|
573
|
+
function onUsernameChange(value) {
|
|
574
|
+
const isValid = formValidators.username.check(value);
|
|
575
|
+
if (!isValid) {
|
|
576
|
+
showError('用户名至少3个字符');
|
|
577
|
+
} else {
|
|
578
|
+
clearError();
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// 提交验证
|
|
583
|
+
function onSubmit(formData) {
|
|
584
|
+
const usernameResult = formValidators.username.validate(formData.username);
|
|
585
|
+
const passwordResult = formValidators.password.validate(formData.password);
|
|
586
|
+
|
|
587
|
+
if (!usernameResult.valid) {
|
|
588
|
+
return alert(usernameResult.errors[0].message);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (!passwordResult.valid) {
|
|
592
|
+
return alert(passwordResult.errors[0].message);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// 提交表单...
|
|
596
|
+
}
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
### 场景4:用户权限检查
|
|
600
|
+
|
|
601
|
+
```javascript
|
|
602
|
+
// 权限验证器
|
|
603
|
+
const hasPermission = dsl.if(d => d.role === 'admin')
|
|
604
|
+
.or(d => d.role === 'moderator')
|
|
605
|
+
.message('权限不足');
|
|
606
|
+
|
|
607
|
+
// 中间件
|
|
608
|
+
function checkPermission(req, res, next) {
|
|
609
|
+
if (!hasPermission.check(req.user)) {
|
|
610
|
+
return res.status(403).json({ error: '权限不足' });
|
|
611
|
+
}
|
|
612
|
+
next();
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// 路由
|
|
616
|
+
app.delete('/users/:id', checkPermission, deleteUser);
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
### 场景5:根据年龄和用户类型验证不同字段(传统方式对比)
|
|
620
|
+
|
|
621
|
+
```javascript
|
|
622
|
+
// 传统方式(需要 validate 函数)
|
|
623
|
+
const schema = dsl({
|
|
624
|
+
username: 'string:3-32!',
|
|
625
|
+
age: 'number:1-120!',
|
|
626
|
+
userType: 'enum:admin|vip|user!',
|
|
627
|
+
|
|
628
|
+
// 未成年禁止注册
|
|
629
|
+
ageCheck: dsl.if((data) => data.age < 18)
|
|
630
|
+
.message('未成年用户不能注册'),
|
|
631
|
+
|
|
632
|
+
// 管理员必须有邮箱
|
|
633
|
+
email: dsl.if((data) => data.userType === 'admin')
|
|
634
|
+
.then('email!')
|
|
635
|
+
.else('email'),
|
|
636
|
+
|
|
637
|
+
// VIP用户必须有手机号
|
|
638
|
+
phone: dsl.if((data) => data.userType === 'vip')
|
|
639
|
+
.then('string:11!')
|
|
640
|
+
.else(null),
|
|
641
|
+
|
|
642
|
+
// 管理员和VIP可以设置昵称
|
|
643
|
+
nickname: dsl.if((data) => data.userType === 'admin')
|
|
644
|
+
.or((data) => data.userType === 'vip')
|
|
645
|
+
.then('string:2-20')
|
|
646
|
+
.else(null)
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
// 测试
|
|
650
|
+
validate(schema, {
|
|
651
|
+
username: 'admin1',
|
|
652
|
+
age: 25,
|
|
653
|
+
userType: 'admin',
|
|
654
|
+
email: 'admin@example.com'
|
|
655
|
+
});
|
|
656
|
+
// => { valid: true }
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
### 场景2:商品发布
|
|
660
|
+
|
|
661
|
+
根据商品类型验证不同字段。
|
|
662
|
+
|
|
663
|
+
```javascript
|
|
664
|
+
const schema = dsl({
|
|
665
|
+
title: 'string:1-100!',
|
|
666
|
+
price: 'number:0-!',
|
|
667
|
+
type: 'enum:physical|digital|service!',
|
|
668
|
+
|
|
669
|
+
// 实体商品需要重量和尺寸
|
|
670
|
+
weight: dsl.if((data) => data.type === 'physical')
|
|
671
|
+
.then('number:0-!')
|
|
672
|
+
.else(null),
|
|
673
|
+
|
|
674
|
+
dimensions: dsl.if((data) => data.type === 'physical')
|
|
675
|
+
.then('string!')
|
|
676
|
+
.else(null),
|
|
677
|
+
|
|
678
|
+
// 数字商品需要下载链接
|
|
679
|
+
downloadUrl: dsl.if((data) => data.type === 'digital')
|
|
680
|
+
.then('url!')
|
|
681
|
+
.else(null),
|
|
682
|
+
|
|
683
|
+
// 服务类需要服务时长
|
|
684
|
+
duration: dsl.if((data) => data.type === 'service')
|
|
685
|
+
.then('number:1-!')
|
|
686
|
+
.else(null)
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
// 实体商品
|
|
690
|
+
validate(schema, {
|
|
691
|
+
title: '笔记本电脑',
|
|
692
|
+
price: 5999,
|
|
693
|
+
type: 'physical',
|
|
694
|
+
weight: 1.5,
|
|
695
|
+
dimensions: '30x20x2cm'
|
|
696
|
+
});
|
|
697
|
+
// => { valid: true }
|
|
698
|
+
|
|
699
|
+
// 数字商品
|
|
700
|
+
validate(schema, {
|
|
701
|
+
title: '电子书',
|
|
702
|
+
price: 29.9,
|
|
703
|
+
type: 'digital',
|
|
704
|
+
downloadUrl: 'https://example.com/download'
|
|
705
|
+
});
|
|
706
|
+
// => { valid: true }
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
### 场景3:权限控制
|
|
710
|
+
|
|
711
|
+
根据用户角色和状态控制访问。
|
|
712
|
+
|
|
713
|
+
```javascript
|
|
714
|
+
const schema = dsl({
|
|
715
|
+
userId: 'string!',
|
|
716
|
+
role: 'enum:admin|moderator|user!',
|
|
717
|
+
status: 'enum:active|suspended|banned!',
|
|
718
|
+
|
|
719
|
+
// 被封禁用户禁止操作
|
|
720
|
+
accessCheck: dsl.if((data) => data.status === 'banned')
|
|
721
|
+
.message('您的账号已被封禁'),
|
|
722
|
+
|
|
723
|
+
// 暂停用户只能查看
|
|
724
|
+
operationType: dsl.if((data) => data.status === 'suspended')
|
|
725
|
+
.then('enum:view!')
|
|
726
|
+
.else('enum:view|edit|delete!'),
|
|
727
|
+
|
|
728
|
+
// 管理员可以访问所有资源
|
|
729
|
+
resourceIds: dsl.if((data) => data.role === 'admin')
|
|
730
|
+
.then('array<string>') // 可选
|
|
731
|
+
.else('array<string>!') // 必填
|
|
732
|
+
});
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
---
|
|
736
|
+
|
|
737
|
+
## 最佳实践
|
|
738
|
+
|
|
739
|
+
### 1. 条件函数保持简单
|
|
740
|
+
|
|
741
|
+
❌ **不推荐**:
|
|
742
|
+
```javascript
|
|
743
|
+
dsl.if((data) => {
|
|
744
|
+
const user = getUserFromDB(data.userId); // 同步数据库查询
|
|
745
|
+
return user.level > 5;
|
|
746
|
+
})
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
✅ **推荐**:
|
|
750
|
+
```javascript
|
|
751
|
+
dsl.if((data) => data.userLevel > 5)
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
**原因**: 条件函数应该只读取数据对象,不应该有副作用或执行耗时操作。
|
|
755
|
+
|
|
756
|
+
---
|
|
757
|
+
|
|
758
|
+
### 2. 使用有意义的字段名
|
|
759
|
+
|
|
760
|
+
❌ **不推荐**:
|
|
761
|
+
```javascript
|
|
762
|
+
const schema = dsl({
|
|
763
|
+
field1: 'string!',
|
|
764
|
+
check1: dsl.if((data) => data.field1 === 'admin')
|
|
765
|
+
.message('Error')
|
|
766
|
+
});
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
✅ **推荐**:
|
|
770
|
+
```javascript
|
|
771
|
+
const schema = dsl({
|
|
772
|
+
userType: 'string!',
|
|
773
|
+
ageVerification: dsl.if((data) => data.age < 18)
|
|
774
|
+
.message('未成年用户不能注册')
|
|
775
|
+
});
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
---
|
|
779
|
+
|
|
780
|
+
### 3. 合理使用 else
|
|
781
|
+
|
|
782
|
+
当条件不满足时需要不同的验证规则,使用 `.else()`:
|
|
783
|
+
|
|
784
|
+
```javascript
|
|
785
|
+
dsl.if((data) => data.userType === 'admin')
|
|
786
|
+
.then('email!')
|
|
787
|
+
.else('email') // 不同的验证规则
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
当条件不满足时不需要验证,省略 `.else()`:
|
|
791
|
+
|
|
792
|
+
```javascript
|
|
793
|
+
dsl.if((data) => data.userType === 'vip')
|
|
794
|
+
.then('string:6!')
|
|
795
|
+
// 不写 else,非 vip 用户不验证
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
---
|
|
799
|
+
|
|
800
|
+
### 4. 多条件组合优先使用函数内部逻辑
|
|
801
|
+
|
|
802
|
+
简单条件可以直接在函数内部组合:
|
|
803
|
+
|
|
804
|
+
```javascript
|
|
805
|
+
// ✅ 推荐(简洁)
|
|
806
|
+
dsl.if((data) => data.age >= 18 && data.userType === 'admin')
|
|
807
|
+
.then('email!')
|
|
808
|
+
|
|
809
|
+
// ⚠️ 可用但稍繁琐
|
|
810
|
+
dsl.if((data) => data.age >= 18)
|
|
811
|
+
.and((data) => data.userType === 'admin')
|
|
812
|
+
.then('email!')
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
复杂逻辑或需要可维护性时使用 `.and()` / `.or()`:
|
|
816
|
+
|
|
817
|
+
```javascript
|
|
818
|
+
// ✅ 推荐(可读性强)
|
|
819
|
+
dsl.if((data) => data.age >= 18)
|
|
820
|
+
.and((data) => data.userType === 'admin')
|
|
821
|
+
.and((data) => data.verified)
|
|
822
|
+
.or((data) => data.isSuperUser)
|
|
823
|
+
.then('email!')
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
---
|
|
827
|
+
|
|
828
|
+
### 5. 错误消息清晰明确
|
|
829
|
+
|
|
830
|
+
❌ **不推荐**:
|
|
831
|
+
```javascript
|
|
832
|
+
dsl.if((data) => data.age < 18)
|
|
833
|
+
.message('Error')
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
✅ **推荐**:
|
|
837
|
+
```javascript
|
|
838
|
+
dsl.if((data) => data.age < 18)
|
|
839
|
+
.message('未成年用户不能注册')
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
✅ **更好**(支持多语言):
|
|
843
|
+
```javascript
|
|
844
|
+
dsl.if((data) => data.age < 18)
|
|
845
|
+
.message('error.user.underage')
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
---
|
|
849
|
+
|
|
850
|
+
## 常见问题
|
|
851
|
+
|
|
852
|
+
### Q1: 条件函数什么时候执行?
|
|
853
|
+
|
|
854
|
+
**A**: 在调用 `validate()` 时执行,不是在定义 Schema 时。
|
|
855
|
+
|
|
856
|
+
```javascript
|
|
857
|
+
const schema = dsl({
|
|
858
|
+
email: dsl.if((data) => data.userType === 'admin')
|
|
859
|
+
.then('email!') // ← 这里不会执行
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
validate(schema, data); // ← 条件函数在这里执行
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
---
|
|
866
|
+
|
|
867
|
+
### Q2: 条件函数可以访问哪些数据?
|
|
868
|
+
|
|
869
|
+
**A**: 可以访问完整的数据对象。
|
|
870
|
+
|
|
871
|
+
```javascript
|
|
872
|
+
const schema = dsl({
|
|
873
|
+
age: 'number!',
|
|
874
|
+
userType: 'string!',
|
|
875
|
+
status: 'string!',
|
|
876
|
+
email: dsl.if((data) => {
|
|
877
|
+
// 可以访问所有字段
|
|
878
|
+
return data.age >= 18 && data.userType === 'admin' && data.status === 'active';
|
|
879
|
+
}).then('email!')
|
|
880
|
+
});
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
---
|
|
884
|
+
|
|
885
|
+
### Q3: 如何处理条件函数抛错?
|
|
886
|
+
|
|
887
|
+
**A**: 条件函数抛错会被捕获,视为条件不满足。
|
|
888
|
+
|
|
889
|
+
```javascript
|
|
890
|
+
const schema = dsl({
|
|
891
|
+
obj: 'object!',
|
|
892
|
+
result: dsl.if((data) => data.obj.nested.value > 10)
|
|
893
|
+
.then('string!')
|
|
894
|
+
.else(null)
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
// data.obj.nested 不存在,访问会抛错
|
|
898
|
+
validate(schema, { obj: {} });
|
|
899
|
+
// => { valid: true } 条件不满足,执行 else(null)
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
**建议**: 在条件函数中做好防御性检查:
|
|
903
|
+
|
|
904
|
+
```javascript
|
|
905
|
+
dsl.if((data) => data.obj?.nested?.value > 10)
|
|
906
|
+
.then('string!')
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
---
|
|
910
|
+
|
|
911
|
+
### Q4: 可以嵌套 dsl.if() 吗?
|
|
912
|
+
|
|
913
|
+
**A**: 可以,支持嵌套。
|
|
914
|
+
|
|
915
|
+
```javascript
|
|
916
|
+
const schema = dsl({
|
|
917
|
+
userType: 'string!',
|
|
918
|
+
age: 'number!',
|
|
919
|
+
email: dsl.if((data) => data.userType === 'admin')
|
|
920
|
+
.then(
|
|
921
|
+
dsl.if((data) => data.age >= 18)
|
|
922
|
+
.then('email!')
|
|
923
|
+
.else('email')
|
|
924
|
+
)
|
|
925
|
+
.else('email')
|
|
926
|
+
});
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
---
|
|
930
|
+
|
|
931
|
+
### Q5: 如何与现有的 dsl.match() 方法配合使用?
|
|
932
|
+
|
|
933
|
+
**A**: 可以混用,选择最适合的方法。
|
|
934
|
+
|
|
935
|
+
```javascript
|
|
936
|
+
const schema = dsl({
|
|
937
|
+
// 静态值映射 - 使用 match
|
|
938
|
+
userType: 'enum:admin|vip|user!',
|
|
939
|
+
level: dsl.match('userType', {
|
|
940
|
+
admin: 'enum:high!',
|
|
941
|
+
vip: 'enum:medium!',
|
|
942
|
+
user: 'enum:low!'
|
|
943
|
+
}),
|
|
944
|
+
|
|
945
|
+
// 动态条件判断 - 使用 if
|
|
946
|
+
email: dsl.if((data) => data.userType === 'admin' && data.level === 'high')
|
|
947
|
+
.then('email!')
|
|
948
|
+
.else('email')
|
|
949
|
+
});
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
**选择建议**:
|
|
953
|
+
- **简单值映射** → 使用 `dsl.match()`
|
|
954
|
+
- **复杂条件逻辑** → 使用 `dsl.if()`
|
|
955
|
+
|
|
956
|
+
---
|
|
957
|
+
|
|
958
|
+
### Q6: 是否支持非对象类型(字符串、数组、数字等)?
|
|
959
|
+
|
|
960
|
+
**A**: 完全支持!可以直接验证任何类型的值。
|
|
961
|
+
|
|
962
|
+
```javascript
|
|
963
|
+
// 示例1:直接验证字符串
|
|
964
|
+
const stringSchema = dsl.if((data) => typeof data === 'string' && data.includes('@'))
|
|
965
|
+
.then('email!')
|
|
966
|
+
.else('string:1-50');
|
|
967
|
+
|
|
968
|
+
validate(stringSchema, 'test@example.com'); // ✅ 作为邮箱验证
|
|
969
|
+
validate(stringSchema, 'just a text'); // ✅ 作为普通字符串验证
|
|
970
|
+
|
|
971
|
+
// 示例2:直接验证数组
|
|
972
|
+
const arraySchema = dsl.if((data) => Array.isArray(data) && data.length > 5)
|
|
973
|
+
.message('数组最多5个元素');
|
|
974
|
+
|
|
975
|
+
validate(arraySchema, [1, 2, 3]); // ✅ 通过
|
|
976
|
+
validate(arraySchema, [1, 2, 3, 4, 5, 6]); // ❌ 失败
|
|
977
|
+
|
|
978
|
+
// 示例3:直接验证数字
|
|
979
|
+
const numberSchema = dsl.if((data) => typeof data === 'number' && data < 0)
|
|
980
|
+
.message('不允许负数');
|
|
981
|
+
|
|
982
|
+
validate(numberSchema, 10); // ✅ 通过
|
|
983
|
+
validate(numberSchema, -5); // ❌ 失败
|
|
984
|
+
|
|
985
|
+
// 示例4:自动识别类型(邮箱或手机号)
|
|
986
|
+
const contactSchema = dsl.if((data) => typeof data === 'string' && data.includes('@'))
|
|
987
|
+
.then('email!')
|
|
988
|
+
.else('string:11!');
|
|
989
|
+
|
|
990
|
+
validate(contactSchema, 'user@example.com'); // ✅ 作为邮箱验证
|
|
991
|
+
validate(contactSchema, '13800138000'); // ✅ 作为手机号验证
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
**完整示例**: 参见 `examples/conditional-non-object.js`
|
|
995
|
+
|
|
996
|
+
---
|
|
997
|
+
|
|
998
|
+
### Q7: 性能如何?
|
|
999
|
+
|
|
1000
|
+
**A**: 性能优秀,条件函数执行非常快。
|
|
1001
|
+
|
|
1002
|
+
- 条件函数是纯 JavaScript 函数,执行速度快
|
|
1003
|
+
- 只遍历条件链一次,找到第一个匹配的条件就停止
|
|
1004
|
+
- 支持缓存优化(WeakMap)
|
|
1005
|
+
|
|
1006
|
+
**性能提示**:
|
|
1007
|
+
- 避免在条件函数中执行耗时操作(数据库查询、API 调用)
|
|
1008
|
+
- 将最常见的条件放在前面(if 而不是 elseIf)
|
|
1009
|
+
|
|
1010
|
+
---
|
|
1011
|
+
|
|
1012
|
+
## 更新日志
|
|
1013
|
+
|
|
1014
|
+
### v1.1.0 (2026-01-05)
|
|
1015
|
+
|
|
1016
|
+
- ✅ 新增 `ConditionalBuilder` 类
|
|
1017
|
+
- ✅ 新增 `dsl.if()` 链式条件 API
|
|
1018
|
+
- ✅ 支持 and/or 多条件组合
|
|
1019
|
+
- ✅ 支持 elseIf 多分支
|
|
1020
|
+
- ✅ message 自动抛错(无需 throwError)
|
|
1021
|
+
- ✅ else 可选(不写就不验证)
|
|
1022
|
+
- ❌ 移除无效的旧条件方法类型定义
|
|
1023
|
+
|
|
1024
|
+
---
|
|
1025
|
+
|
|
1026
|
+
## 相关文档
|
|
1027
|
+
|
|
1028
|
+
- [快速开始](./quick-start.md)
|
|
1029
|
+
- [验证指南](./validation-guide.md)
|
|
1030
|
+
- [API 参考](./api-reference.md)
|
|
1031
|
+
- [最佳实践](./best-practices.md)
|
|
1032
|
+
|