Custom Go Type Aliases for JSON Fields

Create custom type aliases and named types in Go for stronger typing of JSON fields. Covers enums, custom marshalers, and domain-specific types.

Patterns

Detailed Explanation

Custom Types for Stronger JSON Typing

Go lets you define named types based on primitives, giving you type safety, custom methods, and domain semantics that raw strings and ints cannot provide.

Enum-Like Types

{
  "id": 1,
  "status": "active",
  "role": "admin",
  "priority": 3
}
type Status string

const (
    StatusActive   Status = "active"
    StatusInactive Status = "inactive"
    StatusPending  Status = "pending"
)

type Role string

const (
    RoleAdmin  Role = "admin"
    RoleEditor Role = "editor"
    RoleViewer Role = "viewer"
)

type Priority int

const (
    PriorityLow    Priority = 1
    PriorityMedium Priority = 2
    PriorityHigh   Priority = 3
)

type Ticket struct {
    ID       int      `json:"id"`
    Status   Status   `json:"status"`
    Role     Role     `json:"role"`
    Priority Priority `json:"priority"`
}

Why Custom Types Matter

Named types prevent mixing up values that are technically the same Go type:

func AssignTicket(role Role, status Status) { ... }
// Cannot accidentally pass a Status where Role is expected

Custom Marshaling

For types that need special serialization, implement json.Marshaler and json.Unmarshaler:

type Cents int

func (c Cents) MarshalJSON() ([]byte, error) {
    dollars := float64(c) / 100.0
    return json.Marshal(dollars)
}

func (c *Cents) UnmarshalJSON(b []byte) error {
    var dollars float64
    if err := json.Unmarshal(b, &dollars); err != nil {
        return err
    }
    *c = Cents(dollars * 100)
    return nil
}

This type stores money as integer cents internally but serializes as decimal dollars in JSON.

Validation on Unmarshal

Custom types can validate values during unmarshaling:

func (s *Status) UnmarshalJSON(b []byte) error {
    var str string
    json.Unmarshal(b, &str)
    switch Status(str) {
    case StatusActive, StatusInactive, StatusPending:
        *s = Status(str)
        return nil
    }
    return fmt.Errorf("invalid status: %s", str)
}

This rejects unknown status values at parse time instead of letting invalid data propagate through your system.

Use Case

Domain-driven design in Go benefits from custom types for enums, money values, and identifiers. They catch bugs at compile time and make the code self-documenting when converting JSON API data.

Try It — JSON to Go Struct Converter

Open full tool