@macroforge/mcp-server
Version:
MCP server for Macroforge documentation and code analysis
166 lines (129 loc) • 6.15 kB
Markdown
# Default
The `Default` macro generates a static `defaultValue()` factory method that creates instances with
default values. This is analogous to Rust's `Default` trait, providing a standard way to create
"zero" or "empty" instances of types.
## Generated Output
| Type | Generated Code | Description |
| ---------- | --------------------------------------------------- | ------------------------------------------------- |
| Class | `static defaultValue()` + `classNameDefaultValue()` | Static factory method + standalone function |
| Enum | `enumNameDefaultValue(): EnumName` | Standalone function returning `` variant |
| Interface | `ifaceNameDefaultValue(): InterfaceName` | Standalone function returning object literal |
| Type Alias | `typeNameDefaultValue(): TypeName` | Standalone function with type-appropriate default |
Names use **camelCase** conversion (e.g., `UserSettings` -> `userSettingsDefaultValue`).
## Default Values by Type
The macro uses Rust-like default semantics:
| Type | Default Value |
| ----------------- | --------------------------------------------------------------------------------- |
| `string` | `""` (empty string) |
| `number` | `0` |
| `boolean` | `false` |
| `bigint` | `0n` |
| `T[]` | `[]` (empty array) |
| `Array<T>` | `[]` (empty array) |
| `Map<K,V>` | `new Map()` |
| `Set<T>` | `new Set()` |
| `Date` | `new Date()` (current time) |
| `T \| null` | `null` |
| Unions (`A \| B`) | Default of the first primitive or literal member, else the first member's default |
| `CustomType` | `customTypeDefaultValue()` (recursive standalone-function call) |
## Field-Level Options
The `` decorator allows specifying explicit default values:
- `(42)` - Use 42 as the default
- `("hello")` - Use "hello" as the default
- `([])` - Use empty array as the default
- `({ value: "test" })` - Named form for complex values
## Example
```typescript before
/** @derive(Default) */
class UserSettings {
/** @default("light") */
theme: string;
/** @default(10) */
pageSize: number;
notifications: boolean; // Uses type default: false
}
```
```typescript after
class UserSettings {
theme: string;
pageSize: number;
notifications: boolean; // Uses type default: false
static defaultValue(): UserSettings {
const instance: UserSettings = Object.create(UserSettings.prototype);
instance.theme = 'light';
instance.pageSize = 10;
instance.notifications = false;
return instance;
}
}
export function userSettingsDefaultValue(): UserSettings {
return UserSettings.defaultValue();
}
```
## Enum Defaults
For enums, mark one variant with ``:
```typescript before
/** @derive(Default) */
enum Status {
/** @default */
Pending,
Active,
Completed
}
```
```typescript after
enum Status {
/** @default */
Pending,
Active,
Completed
}
export function statusDefaultValue(): Status {
return Status.Pending;
}
```
Like Rust's derive, the class default runs no constructor (so it works beside `Decode`'s, which
takes arguments). Each field gets its `(value)`, else its initializer as written, else its
type's default above. An optional field with neither `` nor an initializer is left out, and
a static property is never a field.
```typescript
/** @derive(Default) */
class Retry {
attempts: number = 3; // 3, its initializer
/** @default(500) */
delayMs: number = 100; // 500, @default wins
label?: string; // left out
}
```
## Primitive Aliases
A type alias of a primitive, plain or branded with `$Newtype`, defaults to the primitive's default
above, or to its `(value)`, cast to the alias:
```typescript
/** @derive(Default) */
type Meters = $Newtype<number>;
export function metersDefaultValue(): Meters {
return 0 as Meters;
}
```
An alias with `` validators must give `(value)`: the primitive's default may fail
them, just as a Rust newtype with no valid zero derives no `Default`.
```typescript
/** @derive(Decode, Default) */
/** @endec(positive) */
/** @default(1) */
type Meters = $Newtype<number>;
```
## Error Handling
The macro reports an expansion error only if:
- An enum has no variant marked with ``
- A type alias that is not an object, union or primitive has no `(value)`
- A primitive alias with `` validators has no `(value)`
- A field's `` expression fails to parse
- A function-typed field has neither `(value)` nor an initializer, as a Rust `fn` pointer
has no `Default`
- A field's initializer reads `this`, which a default has no instance for
Missing defaults on custom-typed fields are **not** expansion errors: every type is assumed to
implement Default (Rust-like philosophy), so the macro emits a `typeNameDefaultValue()` call for any
custom type. If a field's type is in the type registry but does not derive `Default`, a warning is
printed to stderr at expansion time and the call is generated anyway. It may then fail at runtime if
the function does not exist.