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,584 +1,584 @@
1
- # TypeScript 使用指南
2
-
3
- > **版本**: schema-dsl v2.0.0-beta.2
4
- > **更新日期**: 2026-05-08
5
- > **重要**: v1.0.6 移除了全局 String 类型扩展以避免类型污染
6
-
7
- ---
8
-
9
- ## 📋 目录
10
-
11
- 1. [快速开始](#1-快速开始)
12
- 2. [TypeScript 中的链式调用](#2-typescript-中的链式调用)
13
- 3. [类型推导最佳实践](#3-类型推导最佳实践)
14
- 4. [完整示例](#4-完整示例)
15
- 5. [常见问题](#5-常见问题)
16
-
17
- ---
18
-
19
- ## 1. 快速开始
20
-
21
- ### 1.1 安装
22
-
23
- ```bash
24
- npm install schema-dsl
25
- ```
26
-
27
- ### 1.2 基础用法
28
-
29
- ```typescript
30
- import { dsl, validate } from 'schema-dsl';
31
-
32
- // 定义 Schema
33
- const userSchema = dsl({
34
- username: 'string:3-32!',
35
- email: 'email!',
36
- age: 'number:18-100'
37
- });
38
-
39
- // 验证数据
40
- const result = validate(userSchema, {
41
- username: 'testuser',
42
- email: 'test@example.com',
43
- age: 25
44
- });
45
-
46
- if (result.valid) {
47
- console.log('验证通过:', result.data);
48
- } else {
49
- console.log('验证失败:', result.errors);
50
- }
51
- ```
52
-
53
- ---
54
-
55
- ## 2. TypeScript 中的链式调用
56
-
57
- ### 2.1 重要变更(v1.0.6)
58
-
59
- **v1.0.6 移除了全局 `interface String` 扩展**,原因:
60
- - ❌ 全局类型扩展会污染原生 String 类型
61
- - ❌ 导致 `trim()`、`toLowerCase()` 等原生方法的类型推断错误
62
- - ❌ 影响所有使用 TypeScript 的项目的类型安全
63
-
64
- **结果**:在 TypeScript 中直接对字符串链式调用会报类型错误:
65
-
66
- ```typescript
67
- // ❌ TypeScript 中会报错(v1.0.6+)
68
- const schema = dsl({
69
- email: 'email!'.label('邮箱') // 类型错误:Property 'label' does not exist on type 'string'
70
- });
71
-
72
- // ✅ JavaScript 中仍然可以正常使用
73
- const schema = dsl({
74
- email: 'email!'.label('邮箱') // 运行时完全正常
75
- });
76
- ```
77
-
78
- ### 2.2 正确用法 ⭐⭐⭐
79
-
80
- **TypeScript 中必须使用 `dsl()` 函数包裹字符串**,才能获得类型提示和链式调用:
81
-
82
- ```typescript
83
- // ✅ 正确:使用 dsl() 包裹(v1.0.6+ 必须)
84
- const schema = dsl({
85
- email: dsl('email!').label('邮箱').pattern(/custom/)
86
- });
87
-
88
- // ✅ 也可以先定义再使用
89
- const emailField = dsl('email!').label('邮箱');
90
- const schema = dsl({ email: emailField });
91
- ```
92
-
93
- **好处**:
94
- - ✅ 获得完整的类型推导和 IDE 自动提示
95
- - ✅ 不污染原生 String 类型(`trim()` 正确返回 `string`)
96
- - ✅ 更好的类型安全和开发体验
97
-
98
- ### 2.3 工作原理
99
-
100
- ```typescript
101
- // dsl(string) 返回 DslBuilder 实例
102
- const emailBuilder = dsl('email!');
103
- // ^? DslBuilder - 完整的类型定义
104
-
105
- // DslBuilder 支持所有链式方法,并有完整类型提示
106
- emailBuilder.label('邮箱')
107
- // ^? IDE 自动提示所有可用方法
108
- .pattern(/^[a-z]+@[a-z]+\.[a-z]+$/)
109
- .error({ required: '邮箱必填' });
110
-
111
- > ℹ️ 当前类型声明优先覆盖稳定链式 API,例如 `label()`、`pattern()`、`error()`、`default()`。
112
- > 某些运行时扩展方法依然可用,但如果类型声明未暴露,建议在 TypeScript 代码里优先改写为上述稳定组合。
113
- ```
114
-
115
- ---
116
-
117
- ## 3. 类型推导最佳实践
118
-
119
- ### 3.1 方式对比
120
-
121
- | 方式 | JavaScript | TypeScript | 类型推导 | 推荐度 |
122
- |------|-----------|-----------|---------|--------|
123
- | 直接字符串 | ✅ 完美 | ⚠️ 可能无提示 | ❌ 弱 | ⭐⭐ |
124
- | dsl() 包裹 | ✅ 完美 | ✅ 完美 | ✅ 强 | ⭐⭐⭐⭐⭐ |
125
- | 先定义再使用 | ✅ 完美 | ✅ 完美 | ✅ 强 | ⭐⭐⭐⭐ |
126
-
127
- ### 3.2 推荐写法
128
-
129
- #### ✅ 方式 1: 内联使用 dsl() 包裹(最推荐)
130
-
131
- ```typescript
132
- const schema = dsl({
133
- username: dsl('string:3-32!')
134
- .pattern(/^[a-zA-Z0-9_]+$/)
135
- .label('用户名'),
136
- .error({ pattern: '只能包含字母、数字和下划线' }),
137
-
138
- email: dsl('email!')
139
- .label('邮箱地址')
140
- .error({ required: '邮箱必填' }),
141
-
142
- age: dsl('number:18-100')
143
- .label('年龄')
144
- });
145
- ```
146
-
147
- **优点**:
148
- - ✅ 完整的类型推导
149
- - ✅ IDE 自动提示所有方法
150
- - ✅ 代码紧凑,逻辑清晰
151
-
152
- #### ✅ 方式 2: 先定义字段,再组合(适合复用)
153
-
154
- ```typescript
155
- // 定义可复用的字段
156
- const emailField = dsl('email!')
157
- .label('邮箱地址')
158
- .error({ required: '邮箱必填' });
159
-
160
- const usernameField = dsl('string:3-32!')
161
- .pattern(/^[a-zA-Z0-9_]+$/)
162
- .label('用户名')
163
- .error({ pattern: '用户名只能包含字母、数字和下划线' });
164
-
165
- // 组合使用
166
- const registrationSchema = dsl({
167
- email: emailField,
168
- username: usernameField,
169
- password: dsl('string:8-64!')
170
- .pattern(/^(?=.*[A-Za-z])(?=.*\d).{8,}$/)
171
- .label('密码')
172
- .error({ pattern: '密码至少 8 位且必须包含字母和数字' })
173
- });
174
-
175
- const loginSchema = dsl({
176
- email: emailField, // 复用
177
- password: dsl('string!').label('密码')
178
- });
179
- ```
180
-
181
- **优点**:
182
- - ✅ 字段定义可复用
183
- - ✅ 代码更模块化
184
- - ✅ 适合大型项目
185
-
186
- #### ❌ 不推荐的写法
187
-
188
- ```typescript
189
- // ❌ 在 TypeScript 中直接使用字符串链式调用
190
- const schema = dsl({
191
- email: 'email!'.label('邮箱') // 可能无类型提示
192
- });
193
-
194
- // ❌ 混合使用(不一致)
195
- const schema = dsl({
196
- email: 'email!'.label('邮箱'), // 字符串扩展
197
- username: dsl('string!').label('用户名') // dsl 包裹
198
- });
199
- ```
200
-
201
- ---
202
-
203
- ## 4. 完整示例
204
-
205
- ### 4.1 用户注册表单
206
-
207
- ```typescript
208
- import { dsl, validateAsync, ValidationError } from 'schema-dsl';
209
-
210
- // 定义 Schema
211
- const registrationSchema = dsl({
212
- profile: dsl({
213
- username: dsl('string:3-32!')
214
- .pattern(/^[a-zA-Z0-9_]+$/)
215
- .label('用户名')
216
- .error({ pattern: '只能包含字母、数字和下划线' }),
217
-
218
- email: dsl('email!')
219
- .label('邮箱地址')
220
- .error({ required: '邮箱必填' }),
221
-
222
- password: dsl('string:8-64!')
223
- .pattern(/^(?=.*[A-Za-z])(?=.*\d).{8,}$/)
224
- .label('密码')
225
- .error({ pattern: '密码至少 8 位且必须包含字母和数字' }),
226
-
227
- age: dsl('number:18-100')
228
- .label('年龄')
229
- }),
230
-
231
- settings: dsl({
232
- emailNotify: dsl('boolean')
233
- .default(true)
234
- .label('邮件通知'),
235
-
236
- language: dsl('string')
237
- .default('zh-CN')
238
- .label('语言设置')
239
- })
240
- });
241
-
242
- // 异步验证(推荐)
243
- async function registerUser(data: any) {
244
- try {
245
- const validData = await validateAsync(registrationSchema, data);
246
- console.log('注册成功:', validData);
247
- return validData;
248
- } catch (error) {
249
- if (error instanceof ValidationError) {
250
- console.log('验证失败:');
251
- error.errors.forEach(err => {
252
- console.log(` - ${err.path}: ${err.message}`);
253
- });
254
- throw error;
255
- }
256
- throw error;
257
- }
258
- }
259
-
260
- // 使用
261
- registerUser({
262
- profile: {
263
- username: 'testuser',
264
- email: 'test@example.com',
265
- password: 'StrongPass123!',
266
- age: 25
267
- },
268
- settings: {
269
- emailNotify: true,
270
- language: 'en-US'
271
- }
272
- });
273
- ```
274
-
275
- ### 4.2 API 请求验证
276
-
277
- ```typescript
278
- import { ValidationError, dsl, validateAsync } from 'schema-dsl';
279
- import express from 'express';
280
-
281
- const app = express();
282
- app.use(express.json());
283
-
284
- // 定义 API Schema
285
- const createUserSchema = dsl({
286
- username: dsl('string:3-32!')
287
- .pattern(/^[a-zA-Z0-9_]+$/)
288
- .label('用户名'),
289
-
290
- email: dsl('email!').label('邮箱'),
291
-
292
- role: dsl('string')
293
- .default('user')
294
- .label('角色')
295
- });
296
-
297
- // 使用中间件
298
- app.post('/api/users', async (req, res) => {
299
- try {
300
- const validData = await validateAsync(createUserSchema, req.body);
301
-
302
- // 创建用户逻辑
303
- const user = await createUser(validData);
304
-
305
- res.json({ success: true, data: user });
306
- } catch (error) {
307
- if (error instanceof ValidationError) {
308
- res.status(400).json({
309
- success: false,
310
- errors: error.errors.map(e => ({
311
- field: e.path,
312
- message: e.message
313
- }))
314
- });
315
- } else {
316
- res.status(500).json({ success: false, message: '服务器错误' });
317
- }
318
- }
319
- });
320
- ```
321
-
322
- ### 4.3 表单字段复用
323
-
324
- ```typescript
325
- import { dsl } from 'schema-dsl';
326
-
327
- // 定义常用字段
328
- const commonFields = {
329
- email: dsl('email!')
330
- .label('邮箱地址')
331
- .error({ required: '邮箱必填' }),
332
-
333
- username: dsl('string:3-32!')
334
- .pattern(/^[a-zA-Z0-9_]+$/)
335
- .label('用户名')
336
- .error({ pattern: '用户名只能包含字母、数字和下划线' }),
337
-
338
- password: dsl('string:8-64!')
339
- .pattern(/^(?=.*[A-Za-z])(?=.*\d).{8,}$/)
340
- .label('密码')
341
- .error({ pattern: '密码至少 8 位且必须包含字母和数字' })
342
- };
343
-
344
- // 注册表单
345
- const registrationSchema = dsl({
346
- ...commonFields,
347
- confirmPassword: dsl('string!')
348
- .label('确认密码')
349
- });
350
-
351
- // 登录表单
352
- const loginSchema = dsl({
353
- email: commonFields.email,
354
- password: dsl('string!').label('密码') // 登录时不需要强密码验证
355
- });
356
-
357
- // 密码重置表单
358
- const resetPasswordSchema = dsl({
359
- email: commonFields.email,
360
- newPassword: commonFields.password,
361
- confirmPassword: dsl('string!').label('确认新密码')
362
- });
363
- ```
364
-
365
- ---
366
-
367
- ## 5. 常见问题
368
-
369
- ### 5.1 为什么 TypeScript 中字符串链式调用没有类型提示?
370
-
371
- **原因**: TypeScript 对全局 `String.prototype` 扩展的类型推导有限制。
372
-
373
- **解决**: 使用 `dsl()` 包裹字符串:
374
-
375
- ```typescript
376
- // ❌ 可能无提示
377
- 'email!'.label('邮箱')
378
-
379
- // ✅ 完整提示
380
- dsl('email!').label('邮箱')
381
- ```
382
-
383
- ### 5.2 JavaScript 用户需要改变写法吗?
384
-
385
- **不需要!** JavaScript 用户可以继续使用字符串链式调用:
386
-
387
- ```javascript
388
- // JavaScript 中完全正常
389
- const schema = dsl({
390
- email: 'email!'.label('邮箱')
391
- });
392
- ```
393
-
394
- ### 5.3 如何在严格模式下使用?
395
-
396
- 在 `tsconfig.json` 中启用严格模式也没问题:
397
-
398
- ```json
399
- {
400
- "compilerOptions": {
401
- "strict": true,
402
- "noImplicitAny": true
403
- }
404
- }
405
- ```
406
-
407
- 只需使用 `dsl()` 包裹即可:
408
-
409
- ```typescript
410
- const schema = dsl({
411
- email: dsl('email!').label('邮箱') // ✅ 严格模式下正常
412
- });
413
- ```
414
-
415
- ### 5.4 如何获取验证后的数据类型?
416
-
417
- 使用泛型参数:
418
-
419
- ```typescript
420
- interface User {
421
- username: string;
422
- email: string;
423
- age?: number;
424
- }
425
-
426
- // 同步验证
427
- const result = validate<User>(userSchema, data);
428
- if (result.valid) {
429
- const user: User = result.data; // ✅ 类型安全
430
- }
431
-
432
- // 异步验证
433
- const validUser = await validateAsync<User>(userSchema, data);
434
- // ^? User - 完整的类型推导
435
- ```
436
-
437
- ### 5.5 如何处理嵌套对象的验证错误?
438
-
439
- ```typescript
440
- try {
441
- await validateAsync(schema, data);
442
- } catch (error) {
443
- if (error instanceof ValidationError) {
444
- // 方式 1: 遍历所有错误
445
- error.errors.forEach(err => {
446
- console.log(`${err.path}: ${err.message}`);
447
- // 输出: profile.username: 用户名至少3个字符
448
- });
449
-
450
- // 方式 2: 获取特定字段错误
451
- const usernameError = error.getFieldError('profile.username');
452
- if (usernameError) {
453
- console.log(usernameError.message);
454
- }
455
-
456
- // 方式 3: 获取所有字段错误映射
457
- const fieldErrors = error.getFieldErrors();
458
- // { 'profile.username': {...}, 'profile.email': {...} }
459
- }
460
- }
461
- ```
462
-
463
- ---
464
-
465
- ## 6. 进阶技巧
466
-
467
- ### 6.1 额外业务规则
468
-
469
- ```typescript
470
- const schema = dsl({
471
- username: dsl('string:3-32!').label('用户名')
472
- });
473
-
474
- const result = await validateAsync(schema, data);
475
- if (result.username === 'admin') {
476
- throw new Error('用户名已存在');
477
- }
478
- ```
479
-
480
- 这种写法的好处是:结构校验仍由 schema-dsl 负责,业务唯一性、数据库查重等规则继续留在 TypeScript 业务层,避免把外部依赖塞进字段声明。
481
-
482
- ### 6.2 条件验证
483
-
484
- ```typescript
485
- const schema = dsl({
486
- userType: dsl('string!').label('用户类型'),
487
-
488
- // 使用 dsl.match() 根据 userType 字段动态验证
489
- companyName: dsl.match('userType', {
490
- 'company': 'string!', // 企业用户必填
491
- '_default': 'string' // 个人用户可选
492
- })
493
- });
494
- ```
495
-
496
- ### 6.3 Schema 复用和扩展
497
-
498
- ```typescript
499
- import { SchemaUtils } from 'schema-dsl';
500
-
501
- // 基础用户 Schema
502
- const baseUserSchema = dsl({
503
- username: dsl('string:3-32!').label('用户名'),
504
- email: dsl('email!').label('邮箱')
505
- });
506
-
507
- // 扩展为管理员 Schema
508
- const adminSchema = SchemaUtils.extend(baseUserSchema, {
509
- role: dsl('string!').default('admin').label('角色'),
510
- permissions: dsl('array<string>').label('权限列表')
511
- });
512
-
513
- // 只选择部分字段
514
- const publicUserSchema = SchemaUtils.pick(
515
- baseUserSchema,
516
- ['username']
517
- );
518
- ```
519
-
520
- ---
521
-
522
- ## 7. 性能优化
523
-
524
- ### 7.1 复用 Schema 与默认缓存
525
-
526
- ```typescript
527
- const schema = dsl({
528
- email: dsl('email!').label('邮箱')
529
- });
530
-
531
- // 多次验证会复用默认 Validator 的编译缓存
532
- await validateAsync(schema, data1);
533
- await validateAsync(schema, data2);
534
- await validateAsync(schema, data3);
535
- ```
536
-
537
- ### 7.2 缓存配置
538
-
539
- ```typescript
540
- import { dsl } from 'schema-dsl';
541
-
542
- // 配置缓存大小
543
- dsl.config({
544
- cache: {
545
- maxSize: 5000, // 缓存条目数
546
- ttl: 60000 // 过期时间(毫秒)
547
- }
548
- });
549
- ```
550
-
551
- ---
552
-
553
- ## 8. 最佳实践总结
554
-
555
- 1. ✅ **TypeScript 中始终使用 `dsl()` 包裹字符串**
556
- 2. ✅ **使用 `validateAsync` 进行异步验证**
557
- 3. ✅ **为验证结果添加泛型类型参数**
558
- 4. ✅ **复用常用字段定义**
559
- 5. ✅ **使用 `ValidationError` 类型守卫处理错误**
560
- 6. ✅ **为用户提供友好的错误消息**
561
- 7. ✅ **复用常用 Schema 对象,让默认缓存命中**
562
-
563
- ---
564
-
565
- ## 9. 相关资源
566
-
567
- - [API 参考文档](./api-reference.md)
568
- - [DSL 语法完整指南](./dsl-syntax.md)
569
- - [验证规则参考](./validation-guide.md)
570
- - [错误处理指南](./error-handling.md)
571
- - [GitHub 仓库](https://github.com/vextjs/schema-dsl)
572
-
573
- ---
574
-
575
- ## 对应示例文件
576
-
577
- **示例入口**: [typescript-guide.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/typescript-guide.ts)
578
- **说明**: 展示 TypeScript 下推荐的 `dsl()` 包裹写法、`validate<T>()` / `validateAsync<T>()`、以及 `ValidationError` 的字段错误读取方式。
579
-
580
- ---
581
-
582
- **更新日期**: 2026-05-08
583
- **文档版本**: v2.0.0-beta.2
584
-
1
+ # TypeScript 使用指南
2
+
3
+ > **版本**: schema-dsl v2.0.0-beta.2
4
+ > **更新日期**: 2026-05-08
5
+ > **重要**: v1.0.6 移除了全局 String 类型扩展以避免类型污染
6
+
7
+ ---
8
+
9
+ ## 📋 目录
10
+
11
+ 1. [快速开始](#1-快速开始)
12
+ 2. [TypeScript 中的链式调用](#2-typescript-中的链式调用)
13
+ 3. [类型推导最佳实践](#3-类型推导最佳实践)
14
+ 4. [完整示例](#4-完整示例)
15
+ 5. [常见问题](#5-常见问题)
16
+
17
+ ---
18
+
19
+ ## 1. 快速开始
20
+
21
+ ### 1.1 安装
22
+
23
+ ```bash
24
+ npm install schema-dsl
25
+ ```
26
+
27
+ ### 1.2 基础用法
28
+
29
+ ```typescript
30
+ import { dsl, validate } from 'schema-dsl';
31
+
32
+ // 定义 Schema
33
+ const userSchema = dsl({
34
+ username: 'string:3-32!',
35
+ email: 'email!',
36
+ age: 'number:18-100'
37
+ });
38
+
39
+ // 验证数据
40
+ const result = validate(userSchema, {
41
+ username: 'testuser',
42
+ email: 'test@example.com',
43
+ age: 25
44
+ });
45
+
46
+ if (result.valid) {
47
+ console.log('验证通过:', result.data);
48
+ } else {
49
+ console.log('验证失败:', result.errors);
50
+ }
51
+ ```
52
+
53
+ ---
54
+
55
+ ## 2. TypeScript 中的链式调用
56
+
57
+ ### 2.1 重要变更(v1.0.6)
58
+
59
+ **v1.0.6 移除了全局 `interface String` 扩展**,原因:
60
+ - ❌ 全局类型扩展会污染原生 String 类型
61
+ - ❌ 导致 `trim()`、`toLowerCase()` 等原生方法的类型推断错误
62
+ - ❌ 影响所有使用 TypeScript 的项目的类型安全
63
+
64
+ **结果**:在 TypeScript 中直接对字符串链式调用会报类型错误:
65
+
66
+ ```typescript
67
+ // ❌ TypeScript 中会报错(v1.0.6+)
68
+ const schema = dsl({
69
+ email: 'email!'.label('邮箱') // 类型错误:Property 'label' does not exist on type 'string'
70
+ });
71
+
72
+ // ✅ JavaScript 中仍然可以正常使用
73
+ const schema = dsl({
74
+ email: 'email!'.label('邮箱') // 运行时完全正常
75
+ });
76
+ ```
77
+
78
+ ### 2.2 正确用法 ⭐⭐⭐
79
+
80
+ **TypeScript 中必须使用 `dsl()` 函数包裹字符串**,才能获得类型提示和链式调用:
81
+
82
+ ```typescript
83
+ // ✅ 正确:使用 dsl() 包裹(v1.0.6+ 必须)
84
+ const schema = dsl({
85
+ email: dsl('email!').label('邮箱').pattern(/custom/)
86
+ });
87
+
88
+ // ✅ 也可以先定义再使用
89
+ const emailField = dsl('email!').label('邮箱');
90
+ const schema = dsl({ email: emailField });
91
+ ```
92
+
93
+ **好处**:
94
+ - ✅ 获得完整的类型推导和 IDE 自动提示
95
+ - ✅ 不污染原生 String 类型(`trim()` 正确返回 `string`)
96
+ - ✅ 更好的类型安全和开发体验
97
+
98
+ ### 2.3 工作原理
99
+
100
+ ```typescript
101
+ // dsl(string) 返回 DslBuilder 实例
102
+ const emailBuilder = dsl('email!');
103
+ // ^? DslBuilder - 完整的类型定义
104
+
105
+ // DslBuilder 支持所有链式方法,并有完整类型提示
106
+ emailBuilder.label('邮箱')
107
+ // ^? IDE 自动提示所有可用方法
108
+ .pattern(/^[a-z]+@[a-z]+\.[a-z]+$/)
109
+ .error({ required: '邮箱必填' });
110
+
111
+ > ℹ️ 当前类型声明优先覆盖稳定链式 API,例如 `label()`、`pattern()`、`error()`、`default()`。
112
+ > 某些运行时扩展方法依然可用,但如果类型声明未暴露,建议在 TypeScript 代码里优先改写为上述稳定组合。
113
+ ```
114
+
115
+ ---
116
+
117
+ ## 3. 类型推导最佳实践
118
+
119
+ ### 3.1 方式对比
120
+
121
+ | 方式 | JavaScript | TypeScript | 类型推导 | 推荐度 |
122
+ |------|-----------|-----------|---------|--------|
123
+ | 直接字符串 | ✅ 完美 | ⚠️ 可能无提示 | ❌ 弱 | ⭐⭐ |
124
+ | dsl() 包裹 | ✅ 完美 | ✅ 完美 | ✅ 强 | ⭐⭐⭐⭐⭐ |
125
+ | 先定义再使用 | ✅ 完美 | ✅ 完美 | ✅ 强 | ⭐⭐⭐⭐ |
126
+
127
+ ### 3.2 推荐写法
128
+
129
+ #### ✅ 方式 1: 内联使用 dsl() 包裹(最推荐)
130
+
131
+ ```typescript
132
+ const schema = dsl({
133
+ username: dsl('string:3-32!')
134
+ .pattern(/^[a-zA-Z0-9_]+$/)
135
+ .label('用户名'),
136
+ .error({ pattern: '只能包含字母、数字和下划线' }),
137
+
138
+ email: dsl('email!')
139
+ .label('邮箱地址')
140
+ .error({ required: '邮箱必填' }),
141
+
142
+ age: dsl('number:18-100')
143
+ .label('年龄')
144
+ });
145
+ ```
146
+
147
+ **优点**:
148
+ - ✅ 完整的类型推导
149
+ - ✅ IDE 自动提示所有方法
150
+ - ✅ 代码紧凑,逻辑清晰
151
+
152
+ #### ✅ 方式 2: 先定义字段,再组合(适合复用)
153
+
154
+ ```typescript
155
+ // 定义可复用的字段
156
+ const emailField = dsl('email!')
157
+ .label('邮箱地址')
158
+ .error({ required: '邮箱必填' });
159
+
160
+ const usernameField = dsl('string:3-32!')
161
+ .pattern(/^[a-zA-Z0-9_]+$/)
162
+ .label('用户名')
163
+ .error({ pattern: '用户名只能包含字母、数字和下划线' });
164
+
165
+ // 组合使用
166
+ const registrationSchema = dsl({
167
+ email: emailField,
168
+ username: usernameField,
169
+ password: dsl('string:8-64!')
170
+ .pattern(/^(?=.*[A-Za-z])(?=.*\d).{8,}$/)
171
+ .label('密码')
172
+ .error({ pattern: '密码至少 8 位且必须包含字母和数字' })
173
+ });
174
+
175
+ const loginSchema = dsl({
176
+ email: emailField, // 复用
177
+ password: dsl('string!').label('密码')
178
+ });
179
+ ```
180
+
181
+ **优点**:
182
+ - ✅ 字段定义可复用
183
+ - ✅ 代码更模块化
184
+ - ✅ 适合大型项目
185
+
186
+ #### ❌ 不推荐的写法
187
+
188
+ ```typescript
189
+ // ❌ 在 TypeScript 中直接使用字符串链式调用
190
+ const schema = dsl({
191
+ email: 'email!'.label('邮箱') // 可能无类型提示
192
+ });
193
+
194
+ // ❌ 混合使用(不一致)
195
+ const schema = dsl({
196
+ email: 'email!'.label('邮箱'), // 字符串扩展
197
+ username: dsl('string!').label('用户名') // dsl 包裹
198
+ });
199
+ ```
200
+
201
+ ---
202
+
203
+ ## 4. 完整示例
204
+
205
+ ### 4.1 用户注册表单
206
+
207
+ ```typescript
208
+ import { dsl, validateAsync, ValidationError } from 'schema-dsl';
209
+
210
+ // 定义 Schema
211
+ const registrationSchema = dsl({
212
+ profile: dsl({
213
+ username: dsl('string:3-32!')
214
+ .pattern(/^[a-zA-Z0-9_]+$/)
215
+ .label('用户名')
216
+ .error({ pattern: '只能包含字母、数字和下划线' }),
217
+
218
+ email: dsl('email!')
219
+ .label('邮箱地址')
220
+ .error({ required: '邮箱必填' }),
221
+
222
+ password: dsl('string:8-64!')
223
+ .pattern(/^(?=.*[A-Za-z])(?=.*\d).{8,}$/)
224
+ .label('密码')
225
+ .error({ pattern: '密码至少 8 位且必须包含字母和数字' }),
226
+
227
+ age: dsl('number:18-100')
228
+ .label('年龄')
229
+ }),
230
+
231
+ settings: dsl({
232
+ emailNotify: dsl('boolean')
233
+ .default(true)
234
+ .label('邮件通知'),
235
+
236
+ language: dsl('string')
237
+ .default('zh-CN')
238
+ .label('语言设置')
239
+ })
240
+ });
241
+
242
+ // 异步验证(推荐)
243
+ async function registerUser(data: any) {
244
+ try {
245
+ const validData = await validateAsync(registrationSchema, data);
246
+ console.log('注册成功:', validData);
247
+ return validData;
248
+ } catch (error) {
249
+ if (error instanceof ValidationError) {
250
+ console.log('验证失败:');
251
+ error.errors.forEach(err => {
252
+ console.log(` - ${err.path}: ${err.message}`);
253
+ });
254
+ throw error;
255
+ }
256
+ throw error;
257
+ }
258
+ }
259
+
260
+ // 使用
261
+ registerUser({
262
+ profile: {
263
+ username: 'testuser',
264
+ email: 'test@example.com',
265
+ password: 'StrongPass123!',
266
+ age: 25
267
+ },
268
+ settings: {
269
+ emailNotify: true,
270
+ language: 'en-US'
271
+ }
272
+ });
273
+ ```
274
+
275
+ ### 4.2 API 请求验证
276
+
277
+ ```typescript
278
+ import { ValidationError, dsl, validateAsync } from 'schema-dsl';
279
+ import express from 'express';
280
+
281
+ const app = express();
282
+ app.use(express.json());
283
+
284
+ // 定义 API Schema
285
+ const createUserSchema = dsl({
286
+ username: dsl('string:3-32!')
287
+ .pattern(/^[a-zA-Z0-9_]+$/)
288
+ .label('用户名'),
289
+
290
+ email: dsl('email!').label('邮箱'),
291
+
292
+ role: dsl('string')
293
+ .default('user')
294
+ .label('角色')
295
+ });
296
+
297
+ // 使用中间件
298
+ app.post('/api/users', async (req, res) => {
299
+ try {
300
+ const validData = await validateAsync(createUserSchema, req.body);
301
+
302
+ // 创建用户逻辑
303
+ const user = await createUser(validData);
304
+
305
+ res.json({ success: true, data: user });
306
+ } catch (error) {
307
+ if (error instanceof ValidationError) {
308
+ res.status(400).json({
309
+ success: false,
310
+ errors: error.errors.map(e => ({
311
+ field: e.path,
312
+ message: e.message
313
+ }))
314
+ });
315
+ } else {
316
+ res.status(500).json({ success: false, message: '服务器错误' });
317
+ }
318
+ }
319
+ });
320
+ ```
321
+
322
+ ### 4.3 表单字段复用
323
+
324
+ ```typescript
325
+ import { dsl } from 'schema-dsl';
326
+
327
+ // 定义常用字段
328
+ const commonFields = {
329
+ email: dsl('email!')
330
+ .label('邮箱地址')
331
+ .error({ required: '邮箱必填' }),
332
+
333
+ username: dsl('string:3-32!')
334
+ .pattern(/^[a-zA-Z0-9_]+$/)
335
+ .label('用户名')
336
+ .error({ pattern: '用户名只能包含字母、数字和下划线' }),
337
+
338
+ password: dsl('string:8-64!')
339
+ .pattern(/^(?=.*[A-Za-z])(?=.*\d).{8,}$/)
340
+ .label('密码')
341
+ .error({ pattern: '密码至少 8 位且必须包含字母和数字' })
342
+ };
343
+
344
+ // 注册表单
345
+ const registrationSchema = dsl({
346
+ ...commonFields,
347
+ confirmPassword: dsl('string!')
348
+ .label('确认密码')
349
+ });
350
+
351
+ // 登录表单
352
+ const loginSchema = dsl({
353
+ email: commonFields.email,
354
+ password: dsl('string!').label('密码') // 登录时不需要强密码验证
355
+ });
356
+
357
+ // 密码重置表单
358
+ const resetPasswordSchema = dsl({
359
+ email: commonFields.email,
360
+ newPassword: commonFields.password,
361
+ confirmPassword: dsl('string!').label('确认新密码')
362
+ });
363
+ ```
364
+
365
+ ---
366
+
367
+ ## 5. 常见问题
368
+
369
+ ### 5.1 为什么 TypeScript 中字符串链式调用没有类型提示?
370
+
371
+ **原因**: TypeScript 对全局 `String.prototype` 扩展的类型推导有限制。
372
+
373
+ **解决**: 使用 `dsl()` 包裹字符串:
374
+
375
+ ```typescript
376
+ // ❌ 可能无提示
377
+ 'email!'.label('邮箱')
378
+
379
+ // ✅ 完整提示
380
+ dsl('email!').label('邮箱')
381
+ ```
382
+
383
+ ### 5.2 JavaScript 用户需要改变写法吗?
384
+
385
+ **不需要!** JavaScript 用户可以继续使用字符串链式调用:
386
+
387
+ ```javascript
388
+ // JavaScript 中完全正常
389
+ const schema = dsl({
390
+ email: 'email!'.label('邮箱')
391
+ });
392
+ ```
393
+
394
+ ### 5.3 如何在严格模式下使用?
395
+
396
+ 在 `tsconfig.json` 中启用严格模式也没问题:
397
+
398
+ ```json
399
+ {
400
+ "compilerOptions": {
401
+ "strict": true,
402
+ "noImplicitAny": true
403
+ }
404
+ }
405
+ ```
406
+
407
+ 只需使用 `dsl()` 包裹即可:
408
+
409
+ ```typescript
410
+ const schema = dsl({
411
+ email: dsl('email!').label('邮箱') // ✅ 严格模式下正常
412
+ });
413
+ ```
414
+
415
+ ### 5.4 如何获取验证后的数据类型?
416
+
417
+ 使用泛型参数:
418
+
419
+ ```typescript
420
+ interface User {
421
+ username: string;
422
+ email: string;
423
+ age?: number;
424
+ }
425
+
426
+ // 同步验证
427
+ const result = validate<User>(userSchema, data);
428
+ if (result.valid) {
429
+ const user: User = result.data; // ✅ 类型安全
430
+ }
431
+
432
+ // 异步验证
433
+ const validUser = await validateAsync<User>(userSchema, data);
434
+ // ^? User - 完整的类型推导
435
+ ```
436
+
437
+ ### 5.5 如何处理嵌套对象的验证错误?
438
+
439
+ ```typescript
440
+ try {
441
+ await validateAsync(schema, data);
442
+ } catch (error) {
443
+ if (error instanceof ValidationError) {
444
+ // 方式 1: 遍历所有错误
445
+ error.errors.forEach(err => {
446
+ console.log(`${err.path}: ${err.message}`);
447
+ // 输出: profile.username: 用户名至少3个字符
448
+ });
449
+
450
+ // 方式 2: 获取特定字段错误
451
+ const usernameError = error.getFieldError('profile.username');
452
+ if (usernameError) {
453
+ console.log(usernameError.message);
454
+ }
455
+
456
+ // 方式 3: 获取所有字段错误映射
457
+ const fieldErrors = error.getFieldErrors();
458
+ // { 'profile.username': {...}, 'profile.email': {...} }
459
+ }
460
+ }
461
+ ```
462
+
463
+ ---
464
+
465
+ ## 6. 进阶技巧
466
+
467
+ ### 6.1 额外业务规则
468
+
469
+ ```typescript
470
+ const schema = dsl({
471
+ username: dsl('string:3-32!').label('用户名')
472
+ });
473
+
474
+ const result = await validateAsync(schema, data);
475
+ if (result.username === 'admin') {
476
+ throw new Error('用户名已存在');
477
+ }
478
+ ```
479
+
480
+ 这种写法的好处是:结构校验仍由 schema-dsl 负责,业务唯一性、数据库查重等规则继续留在 TypeScript 业务层,避免把外部依赖塞进字段声明。
481
+
482
+ ### 6.2 条件验证
483
+
484
+ ```typescript
485
+ const schema = dsl({
486
+ userType: dsl('string!').label('用户类型'),
487
+
488
+ // 使用 dsl.match() 根据 userType 字段动态验证
489
+ companyName: dsl.match('userType', {
490
+ 'company': 'string!', // 企业用户必填
491
+ '_default': 'string' // 个人用户可选
492
+ })
493
+ });
494
+ ```
495
+
496
+ ### 6.3 Schema 复用和扩展
497
+
498
+ ```typescript
499
+ import { SchemaUtils } from 'schema-dsl';
500
+
501
+ // 基础用户 Schema
502
+ const baseUserSchema = dsl({
503
+ username: dsl('string:3-32!').label('用户名'),
504
+ email: dsl('email!').label('邮箱')
505
+ });
506
+
507
+ // 扩展为管理员 Schema
508
+ const adminSchema = SchemaUtils.extend(baseUserSchema, {
509
+ role: dsl('string!').default('admin').label('角色'),
510
+ permissions: dsl('array<string>').label('权限列表')
511
+ });
512
+
513
+ // 只选择部分字段
514
+ const publicUserSchema = SchemaUtils.pick(
515
+ baseUserSchema,
516
+ ['username']
517
+ );
518
+ ```
519
+
520
+ ---
521
+
522
+ ## 7. 性能优化
523
+
524
+ ### 7.1 复用 Schema 与默认缓存
525
+
526
+ ```typescript
527
+ const schema = dsl({
528
+ email: dsl('email!').label('邮箱')
529
+ });
530
+
531
+ // 多次验证会复用默认 Validator 的编译缓存
532
+ await validateAsync(schema, data1);
533
+ await validateAsync(schema, data2);
534
+ await validateAsync(schema, data3);
535
+ ```
536
+
537
+ ### 7.2 缓存配置
538
+
539
+ ```typescript
540
+ import { dsl } from 'schema-dsl';
541
+
542
+ // 配置缓存大小
543
+ dsl.config({
544
+ cache: {
545
+ maxSize: 5000, // 缓存条目数
546
+ ttl: 60000 // 过期时间(毫秒)
547
+ }
548
+ });
549
+ ```
550
+
551
+ ---
552
+
553
+ ## 8. 最佳实践总结
554
+
555
+ 1. ✅ **TypeScript 中始终使用 `dsl()` 包裹字符串**
556
+ 2. ✅ **使用 `validateAsync` 进行异步验证**
557
+ 3. ✅ **为验证结果添加泛型类型参数**
558
+ 4. ✅ **复用常用字段定义**
559
+ 5. ✅ **使用 `ValidationError` 类型守卫处理错误**
560
+ 6. ✅ **为用户提供友好的错误消息**
561
+ 7. ✅ **复用常用 Schema 对象,让默认缓存命中**
562
+
563
+ ---
564
+
565
+ ## 9. 相关资源
566
+
567
+ - [API 参考文档](./api-reference.md)
568
+ - [DSL 语法完整指南](./dsl-syntax.md)
569
+ - [验证规则参考](./validation-guide.md)
570
+ - [错误处理指南](./error-handling.md)
571
+ - [GitHub 仓库](https://github.com/vextjs/schema-dsl)
572
+
573
+ ---
574
+
575
+ ## 对应示例文件
576
+
577
+ **示例入口**: [typescript-guide.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/typescript-guide.ts)
578
+ **说明**: 展示 TypeScript 下推荐的 `dsl()` 包裹写法、`validate<T>()` / `validateAsync<T>()`、以及 `ValidationError` 的字段错误读取方式。
579
+
580
+ ---
581
+
582
+ **更新日期**: 2026-05-08
583
+ **文档版本**: v2.0.0-beta.2
584
+