···163extern dec errors(target: unknown, ...errors: unknown[]);
164165/**
00000000000000000000000000000000000000000000000000000000000000000166 * Marks a namespace as external, preventing it from emitting JSON output.
167 * This decorator can only be applied to namespaces.
···163extern dec errors(target: unknown, ...errors: unknown[]);
164165/**
166+ * Forces a model, scalar, or union to be inlined instead of creating a standalone def.
167+ * By default, named types create separate definitions with references.
168+ * Use @inline to expand the type inline at each usage site.
169+ *
170+ * @example Inline model
171+ * ```typespec
172+ * @inline
173+ * model Caption {
174+ * text?: string;
175+ * }
176+ *
177+ * model Main {
178+ * captions?: Caption[]; // Expands inline, no separate "caption" def
179+ * }
180+ * ```
181+ *
182+ * @example Inline scalar
183+ * ```typespec
184+ * @inline
185+ * @maxLength(50)
186+ * scalar Handle extends string;
187+ *
188+ * model Main {
189+ * handle?: Handle; // Expands to { type: "string", maxLength: 50 }
190+ * }
191+ * ```
192+ *
193+ * @example Inline union
194+ * ```typespec
195+ * @inline
196+ * union Status { "active", "inactive", string }
197+ *
198+ * model Main {
199+ * status?: Status; // Expands inline with knownValues
200+ * }
201+ * ```
202+ */
203+extern dec inline(target: unknown);
204+205+/**
206+ * Specifies a default value for a scalar or union definition.
207+ * Only valid on standalone scalar or union defs (not @inline).
208+ * The value must match the underlying type (string, integer, or boolean).
209+ * For unions with token refs, you can pass a model reference directly.
210+ *
211+ * @param value - The default value (literal or model reference for tokens)
212+ *
213+ * @example Scalar with default
214+ * ```typespec
215+ * @default("standard")
216+ * scalar Mode extends string;
217+ * ```
218+ *
219+ * @example Union with token default
220+ * ```typespec
221+ * @default(Inperson)
222+ * union EventMode { Hybrid, Inperson, Virtual, string }
223+ *
224+ * @token
225+ * model Inperson {}
226+ * ```
227+ */
228+extern dec `default`(target: unknown, value: unknown);
229+230+/**
231 * Marks a namespace as external, preventing it from emitting JSON output.
232 * This decorator can only be applied to namespaces.
+17
packages/emitter/src/decorators.ts
···25const maxBytesKey = Symbol("maxBytes");
26const minBytesKey = Symbol("minBytes");
27const externalKey = Symbol("external");
02829/**
30 * @maxBytes decorator for maximum length of bytes type
···296 return program.stateSet(readOnlyKey).has(target);
297}
2980000000000000000299/**
300 * @external decorator for marking a namespace as external
301 * External namespaces are skipped during emission and don't produce JSON files
···25const maxBytesKey = Symbol("maxBytes");
26const minBytesKey = Symbol("minBytes");
27const externalKey = Symbol("external");
28+const defaultKey = Symbol("default");
2930/**
31 * @maxBytes decorator for maximum length of bytes type
···297 return program.stateSet(readOnlyKey).has(target);
298}
299300+/**
301+ * @default decorator for setting default values on scalars and unions
302+ * The value can be a literal (string, number, boolean) or a model reference for tokens
303+ */
304+export function $default(context: DecoratorContext, target: Type, value: any) {
305+ // Just store the raw value - let the emitter handle unwrapping and validation
306+ context.program.stateMap(defaultKey).set(target, value);
307+}
308+309+export function getDefault(
310+ program: Program,
311+ target: Type,
312+): any | undefined {
313+ return program.stateMap(defaultKey).get(target);
314+}
315+316/**
317 * @external decorator for marking a namespace as external
318 * External namespaces are skipped during emission and don't produce JSON files
···1+import "@typelex/emitter";
2+3+namespace com.example.scalarDefaults {
4+ /** Test default decorator on scalars */
5+ model Main {
6+ /** Uses string scalar with default */
7+ mode?: Mode;
8+9+ /** Uses integer scalar with default */
10+ limit?: Limit;
11+12+ /** Uses boolean scalar with default */
13+ enabled?: Enabled;
14+ }
15+16+ /** A string type with a default value */
17+ @default("standard")
18+ @maxLength(50)
19+ scalar Mode extends string;
20+21+ /** An integer type with a default value */
22+ @default(50)
23+ @minValue(1)
24+ @maxValue(100)
25+ scalar Limit extends integer;
26+27+ /** A boolean type with a default value */
28+ @default(true)
29+ scalar Enabled extends boolean;
30+}
···312313Note that `Caption` won't exist as a separate def—the abstraction is erased in the output.
31400000000000000000000000000000000000000000000000000000000000000000000000000315## Top-Level Lexicon Types
316317TypeSpec uses `model` for almost everything. Decorators specify what kind of Lexicon type it becomes.
···905906## Defaults and Constants
907908-### Defaults
00909910```typescript
911import "@typelex/emitter";
···920921Maps to: `{"default": 1}`, `{"default": "en"}`
922000000000000000000000000000000000000000000000000000000000000000000000000000000000000000923### Constants
924925Use `@readOnly` with a default:
···312313Note that `Caption` won't exist as a separate def—the abstraction is erased in the output.
314315+### Scalars
316+317+TypeSpec scalars let you create named types with constraints. **By default, scalars create standalone defs** (like models):
318+319+```typescript
320+import "@typelex/emitter";
321+322+namespace com.example {
323+ model Main {
324+ handle?: Handle;
325+ bio?: Bio;
326+ }
327+328+ @maxLength(50)
329+ scalar Handle extends string;
330+331+ @maxLength(256)
332+ @maxGraphemes(128)
333+ scalar Bio extends string;
334+}
335+```
336+337+This creates three defs: `main`, `handle`, and `bio`:
338+339+```json
340+{
341+ "id": "com.example",
342+ "defs": {
343+ "main": {
344+ "type": "object",
345+ "properties": {
346+ "handle": { "type": "ref", "ref": "#handle" },
347+ "bio": { "type": "ref", "ref": "#bio" }
348+ }
349+ },
350+ "handle": {
351+ "type": "string",
352+ "maxLength": 50
353+ },
354+ "bio": {
355+ "type": "string",
356+ "maxLength": 256,
357+ "maxGraphemes": 128
358+ }
359+ }
360+}
361+```
362+363+Use `@inline` to expand a scalar inline instead:
364+365+```typescript
366+import "@typelex/emitter";
367+368+namespace com.example {
369+ model Main {
370+ handle?: Handle;
371+ }
372+373+ @inline
374+ @maxLength(50)
375+ scalar Handle extends string;
376+}
377+```
378+379+Now `Handle` is expanded inline (no separate def):
380+381+```json
382+// ...
383+"properties": {
384+ "handle": { "type": "string", "maxLength": 50 }
385+}
386+// ...
387+```
388+389## Top-Level Lexicon Types
390391TypeSpec uses `model` for almost everything. Decorators specify what kind of Lexicon type it becomes.
···979980## Defaults and Constants
981982+### Property Defaults
983+984+You can set default values on properties:
985986```typescript
987import "@typelex/emitter";
···996997Maps to: `{"default": 1}`, `{"default": "en"}`
998999+### Type Defaults
1000+1001+You can also set defaults on scalar and union types using the `@default` decorator:
1002+1003+```typescript
1004+import "@typelex/emitter";
1005+1006+namespace com.example {
1007+ model Main {
1008+ mode?: Mode;
1009+ priority?: Priority;
1010+ }
1011+1012+ @default("standard")
1013+ scalar Mode extends string;
1014+1015+ @default(1)
1016+ @closed
1017+ @inline
1018+ union Priority { 1, 2, 3 }
1019+}
1020+```
1021+1022+This creates a default on the type definition itself:
1023+1024+```json
1025+{
1026+ "defs": {
1027+ "mode": {
1028+ "type": "string",
1029+ "default": "standard"
1030+ }
1031+ }
1032+}
1033+```
1034+1035+For unions with token references, pass the model directly:
1036+1037+```typescript
1038+import "@typelex/emitter";
1039+1040+namespace com.example {
1041+ model Main {
1042+ eventType?: EventType;
1043+ }
1044+1045+ @default(InPerson)
1046+ union EventType { Hybrid, InPerson, Virtual, string }
1047+1048+ @token model Hybrid {}
1049+ @token model InPerson {}
1050+ @token model Virtual {}
1051+}
1052+```
1053+1054+This resolves to the fully-qualified token NSID:
1055+1056+```json
1057+{
1058+ "eventType": {
1059+ "type": "string",
1060+ "knownValues": [
1061+ "com.example#hybrid",
1062+ "com.example#inPerson",
1063+ "com.example#virtual"
1064+ ],
1065+ "default": "com.example#inPerson"
1066+ }
1067+}
1068+```
1069+1070+**Important:** When a scalar or union creates a standalone def (not `@inline`), property-level defaults must match the type's `@default`. Otherwise you'll get an error:
1071+1072+```typescript
1073+@default("standard")
1074+scalar Mode extends string;
1075+1076+model Main {
1077+ mode?: Mode = "custom"; // ERROR: Conflicting defaults!
1078+}
1079+```
1080+1081+Solutions:
1082+1. Make the defaults match: `mode?: Mode = "standard"`
1083+2. Mark the type `@inline`: Allows property-level defaults
1084+3. Remove the property default: Uses the type's default
1085+1086### Constants
10871088Use `@readOnly` with a default:
···1+import "@typelex/emitter";
2+3+namespace community.lexicon.calendar.event {
4+ /** A calendar event. */
5+ @rec("tid")
6+ model Main {
7+ /** The name of the event. */
8+ @required
9+ name: string;
10+11+ /** The description of the event. */
12+ description?: string;
13+14+ /** Client-declared timestamp when the event was created. */
15+ @required
16+ createdAt: datetime;
17+18+ /** Client-declared timestamp when the event starts. */
19+ startsAt?: datetime;
20+21+ /** Client-declared timestamp when the event ends. */
22+ endsAt?: datetime;
23+24+ /** The attendance mode of the event. */
25+ mode?: Mode;
26+27+ /** The status of the event. */
28+ status?: Status;
29+30+ /** The locations where the event takes place. */
31+ locations?: (
32+ | Uri
33+ | community.lexicon.location.address.Main
34+ | community.lexicon.location.fsq.Main
35+ | community.lexicon.location.geo.Main
36+ | community.lexicon.location.hthree.Main
37+ )[];
38+39+ /** URIs associated with the event. */
40+ uris?: Uri[];
41+ }
42+43+ /** The mode of the event. */
44+ @default(Inperson)
45+ union Mode {
46+ Hybrid,
47+ Inperson,
48+ Virtual,
49+ string,
50+ }
51+52+ /** A virtual event that takes place online. */
53+ @token
54+ model Virtual {}
55+56+ /** An in-person event that takes place offline. */
57+ @token
58+ model Inperson {}
59+60+ /** A hybrid event that takes place both online and offline. */
61+ @token
62+ model Hybrid {}
63+64+ /** The status of the event. */
65+ @default(Scheduled)
66+ union Status {
67+ Cancelled,
68+ Planned,
69+ Postponed,
70+ Rescheduled,
71+ Scheduled,
72+ string,
73+ }
74+75+ /** The event has been created, but not finalized. */
76+ @token
77+ model Planned {}
78+79+ /** The event has been created and scheduled. */
80+ @token
81+ model Scheduled {}
82+83+ /** The event has been rescheduled. */
84+ @token
85+ model Rescheduled {}
86+87+ /** The event has been cancelled. */
88+ @token
89+ model Cancelled {}
90+91+ /** The event has been postponed and a new start date has not been set. */
92+ @token
93+ model Postponed {}
94+95+ /** A URI associated with the event. */
96+ model Uri {
97+ @required
98+ uri: uri;
99+100+ /** The display name of the URI. */
101+ name?: string;
102+ }
103+}
104+105+// --- Externals ---
106+107+@external
108+namespace community.lexicon.location.address {
109+ model Main {}
110+}
111+112+@external
113+namespace community.lexicon.location.fsq {
114+ model Main {}
115+}
116+117+@external
118+namespace community.lexicon.location.geo {
119+ model Main {}
120+}
121+122+@external
123+namespace community.lexicon.location.hthree {
124+ model Main {}
125+}