@type-crafter/mcp 0.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/LICENSE +15 -0
- package/README.md +337 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +354 -0
- package/package.json +74 -0
- package/src/GUIDE.md +365 -0
- package/src/SPEC_RULES.md +970 -0
|
@@ -0,0 +1,970 @@
|
|
|
1
|
+
# Type Crafter YAML Specification Rules
|
|
2
|
+
|
|
3
|
+
Complete guide for writing Type Crafter YAML specifications and understanding TypeScript generation.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Root Structure](#root-structure)
|
|
10
|
+
- [Data Types & Type Mapping](#data-types--type-mapping)
|
|
11
|
+
- [Constraints & Limitations](#constraints--limitations)
|
|
12
|
+
- [Nullable Types (Critical)](#nullable-types-critical)
|
|
13
|
+
- [Type Definitions](#type-definitions)
|
|
14
|
+
- [References](#references)
|
|
15
|
+
- [Composition](#composition)
|
|
16
|
+
- [TypeScript Generation Examples](#typescript-generation-examples)
|
|
17
|
+
- [Best Practices](#best-practices)
|
|
18
|
+
- [Common Patterns](#common-patterns)
|
|
19
|
+
- [Rules Checklist](#rules-checklist)
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Root Structure
|
|
24
|
+
|
|
25
|
+
### Required Fields
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
info:
|
|
29
|
+
version: '0.0.0' # Semver format (required)
|
|
30
|
+
title: 'My API Types' # Specification title (required)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Type Organization
|
|
34
|
+
|
|
35
|
+
**At least ONE is required:**
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
types: {} # Flat/top-level types
|
|
39
|
+
groupedTypes: {} # Grouped/organized types
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Can have both:**
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
types:
|
|
46
|
+
User: {}
|
|
47
|
+
Post: {}
|
|
48
|
+
|
|
49
|
+
groupedTypes:
|
|
50
|
+
Auth:
|
|
51
|
+
LoginRequest: {}
|
|
52
|
+
LoginResponse: {}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Data Types & Type Mapping
|
|
58
|
+
|
|
59
|
+
### Primitive Types → TypeScript Mapping
|
|
60
|
+
|
|
61
|
+
| YAML Type | TypeScript Output | Notes |
|
|
62
|
+
| ----------------------- | ----------------- | ------------------------------ |
|
|
63
|
+
| `string` | `string` | Default string |
|
|
64
|
+
| `string` (format: date) | `Date` | With format specified |
|
|
65
|
+
| `number` | `number` | Floating point |
|
|
66
|
+
| `integer` | `number` | TypeScript has no integer type |
|
|
67
|
+
| `boolean` | `boolean` | |
|
|
68
|
+
| `array` | `Type[]` | Requires `items` specification |
|
|
69
|
+
| `object` | `{ ... }` | Custom object type |
|
|
70
|
+
| `unknown` | `unknown` | For dynamic/any type |
|
|
71
|
+
|
|
72
|
+
### Format Modifiers
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
# String with format
|
|
76
|
+
dateField:
|
|
77
|
+
type: string
|
|
78
|
+
format: date # Generates: Date (not string)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Constraints & Limitations
|
|
84
|
+
|
|
85
|
+
### ❌ Arrays Cannot Be Top-Level Types
|
|
86
|
+
|
|
87
|
+
Arrays can **ONLY** be used as properties within object types, never as standalone top-level types.
|
|
88
|
+
|
|
89
|
+
```yaml
|
|
90
|
+
# ❌ INVALID - This will NOT work
|
|
91
|
+
Tags:
|
|
92
|
+
type: array
|
|
93
|
+
items:
|
|
94
|
+
type: string
|
|
95
|
+
|
|
96
|
+
# ✅ VALID - Arrays must be properties
|
|
97
|
+
Post:
|
|
98
|
+
type: object
|
|
99
|
+
properties:
|
|
100
|
+
tags:
|
|
101
|
+
type: array
|
|
102
|
+
items:
|
|
103
|
+
type: string
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Why:** Type Crafter generates named types. An array alone has no semantic meaning - it must be a property that describes what the array contains.
|
|
107
|
+
|
|
108
|
+
### ✅ Valid Type Categories
|
|
109
|
+
|
|
110
|
+
Only these can be top-level types:
|
|
111
|
+
|
|
112
|
+
- **Objects** (`type: object` with properties)
|
|
113
|
+
- **Enums** (`type: string/number` with enum array)
|
|
114
|
+
- **Primitives** (string, number, boolean, unknown)
|
|
115
|
+
- **Unions** (oneOf)
|
|
116
|
+
- **Intersections** (allOf)
|
|
117
|
+
- **References** ($ref)
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Nullable Types (Critical)
|
|
122
|
+
|
|
123
|
+
### ⚠️ IMPORTANT: Required vs Optional Properties
|
|
124
|
+
|
|
125
|
+
**Properties NOT in `required` array become nullable (`Type | null`)**
|
|
126
|
+
|
|
127
|
+
```yaml
|
|
128
|
+
User:
|
|
129
|
+
type: object
|
|
130
|
+
required:
|
|
131
|
+
- id # Will be: id: string
|
|
132
|
+
- email # Will be: email: string
|
|
133
|
+
properties:
|
|
134
|
+
id:
|
|
135
|
+
type: string
|
|
136
|
+
email:
|
|
137
|
+
type: string
|
|
138
|
+
name:
|
|
139
|
+
type: string # NOT in required → becomes: string | null
|
|
140
|
+
age:
|
|
141
|
+
type: number # NOT in required → becomes: number | null
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**TypeScript Output:**
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
export type User = {
|
|
148
|
+
id: string; // Required - no null
|
|
149
|
+
email: string; // Required - no null
|
|
150
|
+
name: string | null; // Optional - nullable
|
|
151
|
+
age: number | null; // Optional - nullable
|
|
152
|
+
};
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Rules
|
|
156
|
+
|
|
157
|
+
1. ✅ **In `required` array** → `Type`
|
|
158
|
+
2. ✅ **NOT in `required` array** → `Type | null`
|
|
159
|
+
3. ✅ **No `required` array** → All properties are `Type | null`
|
|
160
|
+
4. ❌ **Empty `required: []`** → All properties are `Type | null`
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Type Definitions
|
|
165
|
+
|
|
166
|
+
### Object Type
|
|
167
|
+
|
|
168
|
+
```yaml
|
|
169
|
+
User:
|
|
170
|
+
type: object
|
|
171
|
+
description: 'User account type' # Optional metadata
|
|
172
|
+
example: "{ id: '123', name: 'John' }" # Optional example
|
|
173
|
+
required:
|
|
174
|
+
- id
|
|
175
|
+
- email
|
|
176
|
+
properties:
|
|
177
|
+
id:
|
|
178
|
+
type: string
|
|
179
|
+
description: 'Unique identifier'
|
|
180
|
+
example: 'user-123'
|
|
181
|
+
email:
|
|
182
|
+
type: string
|
|
183
|
+
name:
|
|
184
|
+
type: string
|
|
185
|
+
age:
|
|
186
|
+
type: number
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**TypeScript:**
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
/**
|
|
193
|
+
* @type { User }
|
|
194
|
+
* @description User account type
|
|
195
|
+
* @example { id: '123', name: 'John' }
|
|
196
|
+
*/
|
|
197
|
+
export type User = {
|
|
198
|
+
/**
|
|
199
|
+
* @description Unique identifier
|
|
200
|
+
* @type { string }
|
|
201
|
+
* @memberof User
|
|
202
|
+
* @example user-123
|
|
203
|
+
*/
|
|
204
|
+
id: string;
|
|
205
|
+
email: string;
|
|
206
|
+
name: string | null;
|
|
207
|
+
age: number | null;
|
|
208
|
+
};
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Enum Type (Top-Level)
|
|
212
|
+
|
|
213
|
+
```yaml
|
|
214
|
+
Status:
|
|
215
|
+
type: string
|
|
216
|
+
description: 'User status enum'
|
|
217
|
+
example: 'active'
|
|
218
|
+
enum:
|
|
219
|
+
- active
|
|
220
|
+
- inactive
|
|
221
|
+
- pending
|
|
222
|
+
|
|
223
|
+
Priority:
|
|
224
|
+
type: number
|
|
225
|
+
enum:
|
|
226
|
+
- 1
|
|
227
|
+
- 2
|
|
228
|
+
- 3
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**TypeScript:**
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
// String enum
|
|
235
|
+
export type Status = 'active' | 'inactive' | 'pending';
|
|
236
|
+
|
|
237
|
+
// Number enum
|
|
238
|
+
export type Priority = 1 | 2 | 3;
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Inline Enum (Property)
|
|
242
|
+
|
|
243
|
+
```yaml
|
|
244
|
+
User:
|
|
245
|
+
type: object
|
|
246
|
+
properties:
|
|
247
|
+
role:
|
|
248
|
+
type: string
|
|
249
|
+
enum:
|
|
250
|
+
- admin
|
|
251
|
+
- user
|
|
252
|
+
- guest
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**TypeScript:**
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
export type User = {
|
|
259
|
+
role: ('admin' | 'user' | 'guest') | null;
|
|
260
|
+
};
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Array Types (Property-Level Only)
|
|
264
|
+
|
|
265
|
+
**⚠️ IMPORTANT: Arrays cannot be top-level types. They must be properties within an object type.**
|
|
266
|
+
|
|
267
|
+
```yaml
|
|
268
|
+
# ❌ INVALID - Cannot create standalone array types
|
|
269
|
+
Tags:
|
|
270
|
+
type: array
|
|
271
|
+
items:
|
|
272
|
+
type: string
|
|
273
|
+
|
|
274
|
+
# ✅ VALID - Arrays as properties within objects
|
|
275
|
+
Post:
|
|
276
|
+
type: object
|
|
277
|
+
required:
|
|
278
|
+
- id
|
|
279
|
+
- tags
|
|
280
|
+
properties:
|
|
281
|
+
id:
|
|
282
|
+
type: string
|
|
283
|
+
tags:
|
|
284
|
+
type: array
|
|
285
|
+
items:
|
|
286
|
+
type: string
|
|
287
|
+
comments:
|
|
288
|
+
type: array
|
|
289
|
+
items:
|
|
290
|
+
type: object
|
|
291
|
+
properties:
|
|
292
|
+
text:
|
|
293
|
+
type: string
|
|
294
|
+
relatedPosts:
|
|
295
|
+
type: array
|
|
296
|
+
items:
|
|
297
|
+
$ref: '#/types/Post'
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**TypeScript:**
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
// Arrays only appear as properties, never as top-level types
|
|
304
|
+
export type Post = {
|
|
305
|
+
id: string;
|
|
306
|
+
tags: string[];
|
|
307
|
+
comments: { text: string | null }[] | null;
|
|
308
|
+
relatedPosts: Post[] | null;
|
|
309
|
+
};
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Nested Objects
|
|
313
|
+
|
|
314
|
+
```yaml
|
|
315
|
+
Company:
|
|
316
|
+
type: object
|
|
317
|
+
required:
|
|
318
|
+
- id
|
|
319
|
+
- address
|
|
320
|
+
properties:
|
|
321
|
+
id:
|
|
322
|
+
type: string
|
|
323
|
+
address:
|
|
324
|
+
type: object
|
|
325
|
+
required:
|
|
326
|
+
- street
|
|
327
|
+
properties:
|
|
328
|
+
street:
|
|
329
|
+
type: string
|
|
330
|
+
city:
|
|
331
|
+
type: string
|
|
332
|
+
zipCode:
|
|
333
|
+
type: string
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
**TypeScript:**
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
export type Company = {
|
|
340
|
+
id: string;
|
|
341
|
+
address: {
|
|
342
|
+
street: string;
|
|
343
|
+
city: string | null;
|
|
344
|
+
zipCode: string | null;
|
|
345
|
+
};
|
|
346
|
+
};
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Additional Properties (Hashmap/Dictionary)
|
|
350
|
+
|
|
351
|
+
```yaml
|
|
352
|
+
# Simple hashmap (any keys → any values)
|
|
353
|
+
SimpleMap:
|
|
354
|
+
type: object
|
|
355
|
+
additionalProperties: true
|
|
356
|
+
|
|
357
|
+
# Typed hashmap (string keys → string values)
|
|
358
|
+
StringMap:
|
|
359
|
+
type: object
|
|
360
|
+
additionalProperties:
|
|
361
|
+
keyType: string
|
|
362
|
+
valueType:
|
|
363
|
+
type: string
|
|
364
|
+
|
|
365
|
+
# Number keys → Object values
|
|
366
|
+
IdMap:
|
|
367
|
+
type: object
|
|
368
|
+
additionalProperties:
|
|
369
|
+
keyType: number
|
|
370
|
+
valueType:
|
|
371
|
+
$ref: '#/types/User'
|
|
372
|
+
|
|
373
|
+
# Properties + additional (mixed)
|
|
374
|
+
UserWithMeta:
|
|
375
|
+
type: object
|
|
376
|
+
required:
|
|
377
|
+
- id
|
|
378
|
+
properties:
|
|
379
|
+
id:
|
|
380
|
+
type: string
|
|
381
|
+
additionalProperties:
|
|
382
|
+
keyType: string
|
|
383
|
+
valueType:
|
|
384
|
+
type: string
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
**TypeScript:**
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
export type SimpleMap = {
|
|
391
|
+
[keys: string]: unknown;
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
export type StringMap = {
|
|
395
|
+
[keys: string]: string;
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
export type IdMap = {
|
|
399
|
+
[keys: number]: User;
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
export type UserWithMeta = {
|
|
403
|
+
id: string;
|
|
404
|
+
[keys: string]: string; // Additional dynamic properties
|
|
405
|
+
};
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
## References
|
|
411
|
+
|
|
412
|
+
### Local References (Same File)
|
|
413
|
+
|
|
414
|
+
```yaml
|
|
415
|
+
# Reference to top-level type
|
|
416
|
+
Post:
|
|
417
|
+
type: object
|
|
418
|
+
properties:
|
|
419
|
+
author:
|
|
420
|
+
$ref: '#/types/User'
|
|
421
|
+
|
|
422
|
+
# Reference to grouped type
|
|
423
|
+
Comment:
|
|
424
|
+
type: object
|
|
425
|
+
properties:
|
|
426
|
+
author:
|
|
427
|
+
$ref: '#/groupedTypes/Auth/UserProfile'
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
**Reference Format:**
|
|
431
|
+
|
|
432
|
+
- Top-level: `#/types/TypeName`
|
|
433
|
+
- Grouped: `#/groupedTypes/GroupName/TypeName`
|
|
434
|
+
|
|
435
|
+
### External File References
|
|
436
|
+
|
|
437
|
+
**⚠️ IMPORTANT: Paths are from project root, not relative to current file**
|
|
438
|
+
|
|
439
|
+
```yaml
|
|
440
|
+
# Reference to type in another file (path from project root)
|
|
441
|
+
Post:
|
|
442
|
+
type: object
|
|
443
|
+
properties:
|
|
444
|
+
author:
|
|
445
|
+
$ref: './src/types/users.yaml#/User'
|
|
446
|
+
|
|
447
|
+
# Reference to grouped type in another file
|
|
448
|
+
Comment:
|
|
449
|
+
type: object
|
|
450
|
+
properties:
|
|
451
|
+
metadata:
|
|
452
|
+
$ref: './docs/specs/common.yaml#/SharedTypes/Metadata'
|
|
453
|
+
|
|
454
|
+
# Real-world example: Shopify cart referencing cart item
|
|
455
|
+
SCart:
|
|
456
|
+
type: object
|
|
457
|
+
properties:
|
|
458
|
+
items:
|
|
459
|
+
type: array
|
|
460
|
+
items:
|
|
461
|
+
$ref: './docs/specs/Cart.yaml#/Cart/SCartItem'
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
**Reference Format:**
|
|
465
|
+
|
|
466
|
+
- **Path from project root:** `./path/from/root/file.yaml#/TypeName`
|
|
467
|
+
- External type: `./path/from/root/file.yaml#/TypeName`
|
|
468
|
+
- External grouped: `./path/from/root/file.yaml#/GroupName/TypeName`
|
|
469
|
+
|
|
470
|
+
**Path Rules:**
|
|
471
|
+
|
|
472
|
+
- Paths start with `./` and are relative to **project root** (where you run the command)
|
|
473
|
+
- NOT relative to the current YAML file location
|
|
474
|
+
- Example: If your spec is at `./src/api.yaml` and references `./src/types/user.yaml`, the path is `./src/types/user.yaml`, not `./types/user.yaml`
|
|
475
|
+
|
|
476
|
+
### Group References (Entire Group)
|
|
477
|
+
|
|
478
|
+
```yaml
|
|
479
|
+
groupedTypes:
|
|
480
|
+
# Import entire group from external file
|
|
481
|
+
SharedModels:
|
|
482
|
+
$ref: './shared.yaml#/Models'
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### Cyclic References (Self-Referencing)
|
|
486
|
+
|
|
487
|
+
```yaml
|
|
488
|
+
TreeNode:
|
|
489
|
+
type: object
|
|
490
|
+
properties:
|
|
491
|
+
value:
|
|
492
|
+
type: string
|
|
493
|
+
children:
|
|
494
|
+
type: array
|
|
495
|
+
items:
|
|
496
|
+
$ref: '#/types/TreeNode' # Self-reference allowed
|
|
497
|
+
|
|
498
|
+
LinkedList:
|
|
499
|
+
type: object
|
|
500
|
+
properties:
|
|
501
|
+
value:
|
|
502
|
+
type: number
|
|
503
|
+
next:
|
|
504
|
+
$ref: '#/types/LinkedList' # Self-reference allowed
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
**TypeScript:**
|
|
508
|
+
|
|
509
|
+
```typescript
|
|
510
|
+
export type TreeNode = {
|
|
511
|
+
value: string | null;
|
|
512
|
+
children: TreeNode[] | null;
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
export type LinkedList = {
|
|
516
|
+
value: number | null;
|
|
517
|
+
next: LinkedList | null;
|
|
518
|
+
};
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
## Composition
|
|
524
|
+
|
|
525
|
+
### oneOf (Union Types)
|
|
526
|
+
|
|
527
|
+
Creates TypeScript union: `TypeA | TypeB | TypeC`
|
|
528
|
+
|
|
529
|
+
```yaml
|
|
530
|
+
Response:
|
|
531
|
+
oneOf:
|
|
532
|
+
# Reference types
|
|
533
|
+
- $ref: '#/types/SuccessResponse'
|
|
534
|
+
- $ref: '#/types/ErrorResponse'
|
|
535
|
+
# Primitive types
|
|
536
|
+
- type: string
|
|
537
|
+
- type: number
|
|
538
|
+
# Inline enum
|
|
539
|
+
- type: string
|
|
540
|
+
enum:
|
|
541
|
+
- pending
|
|
542
|
+
- loading
|
|
543
|
+
# Inline object
|
|
544
|
+
- type: object
|
|
545
|
+
properties:
|
|
546
|
+
status:
|
|
547
|
+
type: string
|
|
548
|
+
# Array type
|
|
549
|
+
- type: array
|
|
550
|
+
items:
|
|
551
|
+
type: string
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
**TypeScript:**
|
|
555
|
+
|
|
556
|
+
```typescript
|
|
557
|
+
export type Response =
|
|
558
|
+
| SuccessResponse
|
|
559
|
+
| ErrorResponse
|
|
560
|
+
| string
|
|
561
|
+
| number
|
|
562
|
+
| ('pending' | 'loading')
|
|
563
|
+
| { status: string | null }
|
|
564
|
+
| string[];
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### allOf (Intersection/Merge Types)
|
|
568
|
+
|
|
569
|
+
Creates TypeScript intersection: `TypeA & TypeB & TypeC`
|
|
570
|
+
|
|
571
|
+
```yaml
|
|
572
|
+
AdminUser:
|
|
573
|
+
allOf:
|
|
574
|
+
- $ref: '#/types/BaseUser'
|
|
575
|
+
- $ref: '#/types/AdminPermissions'
|
|
576
|
+
- type: object
|
|
577
|
+
properties:
|
|
578
|
+
adminLevel:
|
|
579
|
+
type: number
|
|
580
|
+
|
|
581
|
+
# Output merges all types together
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
**TypeScript:**
|
|
585
|
+
|
|
586
|
+
```typescript
|
|
587
|
+
export type AdminUser = BaseUser &
|
|
588
|
+
AdminPermissions & {
|
|
589
|
+
adminLevel: number | null;
|
|
590
|
+
};
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
---
|
|
594
|
+
|
|
595
|
+
## TypeScript Generation Examples
|
|
596
|
+
|
|
597
|
+
### Complete Example
|
|
598
|
+
|
|
599
|
+
**YAML:**
|
|
600
|
+
|
|
601
|
+
```yaml
|
|
602
|
+
info:
|
|
603
|
+
version: '1.0.0'
|
|
604
|
+
title: 'Blog API Types'
|
|
605
|
+
|
|
606
|
+
types:
|
|
607
|
+
User:
|
|
608
|
+
type: object
|
|
609
|
+
description: 'Blog user account'
|
|
610
|
+
required:
|
|
611
|
+
- id
|
|
612
|
+
- email
|
|
613
|
+
- role
|
|
614
|
+
properties:
|
|
615
|
+
id:
|
|
616
|
+
type: string
|
|
617
|
+
description: 'Unique user ID'
|
|
618
|
+
email:
|
|
619
|
+
type: string
|
|
620
|
+
role:
|
|
621
|
+
$ref: '#/types/UserRole'
|
|
622
|
+
profile:
|
|
623
|
+
type: object
|
|
624
|
+
properties:
|
|
625
|
+
name:
|
|
626
|
+
type: string
|
|
627
|
+
avatar:
|
|
628
|
+
type: string
|
|
629
|
+
|
|
630
|
+
UserRole:
|
|
631
|
+
type: string
|
|
632
|
+
enum:
|
|
633
|
+
- admin
|
|
634
|
+
- editor
|
|
635
|
+
- viewer
|
|
636
|
+
|
|
637
|
+
Post:
|
|
638
|
+
type: object
|
|
639
|
+
required:
|
|
640
|
+
- id
|
|
641
|
+
- title
|
|
642
|
+
- author
|
|
643
|
+
properties:
|
|
644
|
+
id:
|
|
645
|
+
type: string
|
|
646
|
+
title:
|
|
647
|
+
type: string
|
|
648
|
+
content:
|
|
649
|
+
type: string
|
|
650
|
+
author:
|
|
651
|
+
$ref: '#/types/User'
|
|
652
|
+
tags:
|
|
653
|
+
type: array
|
|
654
|
+
items:
|
|
655
|
+
type: string
|
|
656
|
+
metadata:
|
|
657
|
+
type: object
|
|
658
|
+
additionalProperties:
|
|
659
|
+
keyType: string
|
|
660
|
+
valueType:
|
|
661
|
+
type: string
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
**TypeScript Output:**
|
|
665
|
+
|
|
666
|
+
```typescript
|
|
667
|
+
/**
|
|
668
|
+
* @type { User }
|
|
669
|
+
* @description Blog user account
|
|
670
|
+
*/
|
|
671
|
+
export type User = {
|
|
672
|
+
/**
|
|
673
|
+
* @description Unique user ID
|
|
674
|
+
* @type { string }
|
|
675
|
+
* @memberof User
|
|
676
|
+
*/
|
|
677
|
+
id: string;
|
|
678
|
+
email: string;
|
|
679
|
+
role: UserRole;
|
|
680
|
+
profile: {
|
|
681
|
+
name: string | null;
|
|
682
|
+
avatar: string | null;
|
|
683
|
+
} | null;
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
export type UserRole = 'admin' | 'editor' | 'viewer';
|
|
687
|
+
|
|
688
|
+
export type Post = {
|
|
689
|
+
id: string;
|
|
690
|
+
title: string;
|
|
691
|
+
content: string | null;
|
|
692
|
+
author: User;
|
|
693
|
+
tags: string[] | null;
|
|
694
|
+
metadata: {
|
|
695
|
+
[keys: string]: string;
|
|
696
|
+
} | null;
|
|
697
|
+
};
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
---
|
|
701
|
+
|
|
702
|
+
## Best Practices
|
|
703
|
+
|
|
704
|
+
### 1. Always Use `required` Array
|
|
705
|
+
|
|
706
|
+
```yaml
|
|
707
|
+
# ❌ BAD - All properties become nullable
|
|
708
|
+
User:
|
|
709
|
+
type: object
|
|
710
|
+
properties:
|
|
711
|
+
id: { type: string }
|
|
712
|
+
email: { type: string }
|
|
713
|
+
|
|
714
|
+
# ✅ GOOD - Explicit required fields
|
|
715
|
+
User:
|
|
716
|
+
type: object
|
|
717
|
+
required:
|
|
718
|
+
- id
|
|
719
|
+
- email
|
|
720
|
+
properties:
|
|
721
|
+
id: { type: string }
|
|
722
|
+
email: { type: string }
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
### 2. Use PascalCase for Type Names
|
|
726
|
+
|
|
727
|
+
```yaml
|
|
728
|
+
# ✅ GOOD
|
|
729
|
+
types:
|
|
730
|
+
UserProfile: {}
|
|
731
|
+
BlogPost: {}
|
|
732
|
+
APIResponse: {}
|
|
733
|
+
|
|
734
|
+
# ❌ AVOID
|
|
735
|
+
types:
|
|
736
|
+
userProfile: {}
|
|
737
|
+
blog_post: {}
|
|
738
|
+
api-response: {}
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### 3. Group Related Types
|
|
742
|
+
|
|
743
|
+
```yaml
|
|
744
|
+
# ✅ GOOD
|
|
745
|
+
groupedTypes:
|
|
746
|
+
Auth:
|
|
747
|
+
LoginRequest: {}
|
|
748
|
+
LoginResponse: {}
|
|
749
|
+
RefreshToken: {}
|
|
750
|
+
|
|
751
|
+
Users:
|
|
752
|
+
UserProfile: {}
|
|
753
|
+
UserSettings: {}
|
|
754
|
+
UserPreferences: {}
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
### 4. Use Descriptions for Complex Types
|
|
758
|
+
|
|
759
|
+
```yaml
|
|
760
|
+
# ✅ GOOD
|
|
761
|
+
User:
|
|
762
|
+
type: object
|
|
763
|
+
description: 'Represents a registered user account with authentication details'
|
|
764
|
+
properties:
|
|
765
|
+
passwordHash:
|
|
766
|
+
type: string
|
|
767
|
+
description: 'Bcrypt hashed password (never expose in responses)'
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### 5. Reference External Types for Reusability
|
|
771
|
+
|
|
772
|
+
```yaml
|
|
773
|
+
# ✅ GOOD - Reuse common types (paths from project root)
|
|
774
|
+
Post:
|
|
775
|
+
type: object
|
|
776
|
+
properties:
|
|
777
|
+
author:
|
|
778
|
+
$ref: './src/types/common.yaml#/User'
|
|
779
|
+
metadata:
|
|
780
|
+
$ref: './src/types/common.yaml#/Metadata'
|
|
781
|
+
|
|
782
|
+
# Real example: Shopify cart referencing item types
|
|
783
|
+
SCart:
|
|
784
|
+
type: object
|
|
785
|
+
properties:
|
|
786
|
+
items:
|
|
787
|
+
type: array
|
|
788
|
+
items:
|
|
789
|
+
$ref: './docs/specs/Cart.yaml#/Cart/SCartItem'
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
**Remember:** Paths are from project root, not relative to current file location.
|
|
793
|
+
|
|
794
|
+
---
|
|
795
|
+
|
|
796
|
+
## Common Patterns
|
|
797
|
+
|
|
798
|
+
### Pattern 1: Paginated Response
|
|
799
|
+
|
|
800
|
+
```yaml
|
|
801
|
+
PaginatedUsers:
|
|
802
|
+
type: object
|
|
803
|
+
required:
|
|
804
|
+
- data
|
|
805
|
+
- total
|
|
806
|
+
- page
|
|
807
|
+
properties:
|
|
808
|
+
data:
|
|
809
|
+
type: array
|
|
810
|
+
items:
|
|
811
|
+
$ref: '#/types/User'
|
|
812
|
+
total:
|
|
813
|
+
type: number
|
|
814
|
+
page:
|
|
815
|
+
type: number
|
|
816
|
+
perPage:
|
|
817
|
+
type: number
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
### Pattern 2: API Response Wrapper
|
|
821
|
+
|
|
822
|
+
```yaml
|
|
823
|
+
ApiResponse:
|
|
824
|
+
oneOf:
|
|
825
|
+
- type: object
|
|
826
|
+
required:
|
|
827
|
+
- success
|
|
828
|
+
- data
|
|
829
|
+
properties:
|
|
830
|
+
success:
|
|
831
|
+
type: boolean
|
|
832
|
+
data:
|
|
833
|
+
type: unknown
|
|
834
|
+
- type: object
|
|
835
|
+
required:
|
|
836
|
+
- success
|
|
837
|
+
- error
|
|
838
|
+
properties:
|
|
839
|
+
success:
|
|
840
|
+
type: boolean
|
|
841
|
+
error:
|
|
842
|
+
type: string
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
### Pattern 3: Timestamps Mixin
|
|
846
|
+
|
|
847
|
+
```yaml
|
|
848
|
+
Timestamped:
|
|
849
|
+
type: object
|
|
850
|
+
required:
|
|
851
|
+
- createdAt
|
|
852
|
+
- updatedAt
|
|
853
|
+
properties:
|
|
854
|
+
createdAt:
|
|
855
|
+
type: string
|
|
856
|
+
format: date
|
|
857
|
+
updatedAt:
|
|
858
|
+
type: string
|
|
859
|
+
format: date
|
|
860
|
+
|
|
861
|
+
# Use with allOf
|
|
862
|
+
User:
|
|
863
|
+
allOf:
|
|
864
|
+
- $ref: '#/types/Timestamped'
|
|
865
|
+
- type: object
|
|
866
|
+
required:
|
|
867
|
+
- id
|
|
868
|
+
properties:
|
|
869
|
+
id: { type: string }
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
### Pattern 4: Enum + Metadata
|
|
873
|
+
|
|
874
|
+
```yaml
|
|
875
|
+
StatusType:
|
|
876
|
+
type: string
|
|
877
|
+
enum:
|
|
878
|
+
- draft
|
|
879
|
+
- published
|
|
880
|
+
- archived
|
|
881
|
+
|
|
882
|
+
Post:
|
|
883
|
+
type: object
|
|
884
|
+
required:
|
|
885
|
+
- status
|
|
886
|
+
properties:
|
|
887
|
+
status:
|
|
888
|
+
$ref: '#/types/StatusType'
|
|
889
|
+
statusChangedAt:
|
|
890
|
+
type: string
|
|
891
|
+
format: date
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
---
|
|
895
|
+
|
|
896
|
+
## Rules Checklist
|
|
897
|
+
|
|
898
|
+
### Required ✅
|
|
899
|
+
|
|
900
|
+
- [ ] Has `info.version` (string)
|
|
901
|
+
- [ ] Has `info.title` (string)
|
|
902
|
+
- [ ] Has `types` OR `groupedTypes` (or both)
|
|
903
|
+
|
|
904
|
+
### Constraints ✅
|
|
905
|
+
|
|
906
|
+
- [ ] **Arrays CANNOT be top-level types** - must be properties within objects
|
|
907
|
+
- [ ] Only objects, enums, primitives, unions, intersections, and references can be top-level types
|
|
908
|
+
- [ ] Arrays require `items` specification
|
|
909
|
+
|
|
910
|
+
### Type Definitions ✅
|
|
911
|
+
|
|
912
|
+
- [ ] Objects have `type: object`
|
|
913
|
+
- [ ] Arrays are only used as properties (never top-level)
|
|
914
|
+
- [ ] Enums have `type` (string/number) with `enum` array
|
|
915
|
+
- [ ] Use `required` array for non-nullable properties
|
|
916
|
+
- [ ] Properties not in `required` become `Type | null`
|
|
917
|
+
|
|
918
|
+
### References ✅
|
|
919
|
+
|
|
920
|
+
- [ ] Local references: `#/types/Name` or `#/groupedTypes/Group/Name`
|
|
921
|
+
- [ ] External references: `./path/from/root/file.yaml#/Name`
|
|
922
|
+
- [ ] **External paths are from project root, NOT relative to current file**
|
|
923
|
+
- [ ] Cyclic references are allowed
|
|
924
|
+
|
|
925
|
+
### Composition ✅
|
|
926
|
+
|
|
927
|
+
- [ ] `oneOf` creates unions (`A | B | C`)
|
|
928
|
+
- [ ] `allOf` creates intersections (`A & B & C`)
|
|
929
|
+
- [ ] Can mix inline types and references
|
|
930
|
+
|
|
931
|
+
### TypeScript Generation ✅
|
|
932
|
+
|
|
933
|
+
- [ ] `string` → `string`
|
|
934
|
+
- [ ] `string` (format: date) → `Date`
|
|
935
|
+
- [ ] `number`/`integer` → `number`
|
|
936
|
+
- [ ] `boolean` → `boolean`
|
|
937
|
+
- [ ] `array` → `Type[]`
|
|
938
|
+
- [ ] `object` → `{ ... }`
|
|
939
|
+
- [ ] `unknown` → `unknown`
|
|
940
|
+
- [ ] NOT in `required` → `Type | null`
|
|
941
|
+
|
|
942
|
+
### Best Practices ✅
|
|
943
|
+
|
|
944
|
+
- [ ] Use PascalCase for type names
|
|
945
|
+
- [ ] Always specify `required` array for objects
|
|
946
|
+
- [ ] Group related types in `groupedTypes`
|
|
947
|
+
- [ ] Add descriptions for complex types
|
|
948
|
+
- [ ] Use references for reusability
|
|
949
|
+
- [ ] Prefer explicit over implicit nullability
|
|
950
|
+
|
|
951
|
+
---
|
|
952
|
+
|
|
953
|
+
## Summary
|
|
954
|
+
|
|
955
|
+
| Concept | YAML | TypeScript |
|
|
956
|
+
| ----------------- | ----------------------------- | -------------------- |
|
|
957
|
+
| Required property | In `required` array | `prop: Type` |
|
|
958
|
+
| Optional property | NOT in `required` array | `prop: Type \| null` |
|
|
959
|
+
| String enum | `type: string, enum: [...]` | `'a' \| 'b' \| 'c'` |
|
|
960
|
+
| Number enum | `type: number, enum: [...]` | `1 \| 2 \| 3` |
|
|
961
|
+
| Union | `oneOf: [...]` | `A \| B \| C` |
|
|
962
|
+
| Intersection | `allOf: [...]` | `A & B & C` |
|
|
963
|
+
| Array | `type: array, items: {...}` | `Type[]` |
|
|
964
|
+
| Hashmap | `additionalProperties: {...}` | `[key: Type]: Type` |
|
|
965
|
+
| Reference | `$ref: '#/...'` | Type name |
|
|
966
|
+
| Nested object | Inline `type: object` | `{ ... }` |
|
|
967
|
+
|
|
968
|
+
---
|
|
969
|
+
|
|
970
|
+
**Remember:** Properties NOT in the `required` array automatically become `Type | null` in TypeScript!
|