Enum Type
June 21, 2026 ยท View on GitHub
In Go, defining an enumeration requires two separate declarations: first a
named type with an explicit underlying type, and then a const block whose
values are typed as that named type:
type Color int
const (
ColorRed Color = iota
ColorGreen
ColorBlue
)
This pattern is verbose for a very common need, and it forces the developer
to make an early, often arbitrary, decision about the underlying type
(int, int8, string, bool, etc.) before any values have even been
written down. In practice the underlying type is usually derived from the
values themselves โ if all values are integers, the underlying type is some
integer type; if all values are strings, it's string; and so on.
XGo aims to reduce this boilerplate while remaining fully compatible with Go's type system, AST, and toolchain at the semantic level.
Proposal
Introduce a new declaration form:
type XXX const (
XXXEnum1 = expr1
XXXEnum2 = expr2
...
)
This declares XXX as a new named type whose underlying type is inferred
from the constant expressions inside the block, and simultaneously declares
XXXEnum1, XXXEnum2, ... as named constants of type XXX.
This is semantically equivalent to writing, in Go:
type XXX <inferred underlying type>
const (
XXXEnum1 XXX = expr1
XXXEnum2 XXX = expr2
...
)
but without requiring the author to spell out <inferred underlying type>
or repeat XXX on every line.
Syntax
type Identifier const (
Name1 [= Expr1]
Name2 [= Expr2]
...
)
Identifierbecomes a new named type.- Each
Namebecomes a constant of typeIdentifier. Exprfollows the same rules as ordinaryconstdeclarations, includingiotasupport and the "repeat previous expression" shorthand:
type Weekday const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
This expands to underlying type int (the default type of iota) and is
equivalent to:
type Weekday int
const (
Sunday Weekday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
Underlying Type Inference
The underlying type of XXX is determined by applying Go's normal constant
type-inference rules to the expressions in the block, then taking the
default type of the resulting untyped constant:
| Constant expressions | Inferred underlying type |
|---|---|
Untyped integer constants (0, iota) | int |
Untyped string constants ("a") | string |
Untyped boolean constants (true) | bool |
Untyped float constants (1.5) | float64 |
Untyped rune constants ('a') | rune (int32) |
Rules
-
All expressions must have the same default type. Mixing, e.g., string and integer constants in the same block is a compile-time error:
type Bad const ( A = 1 B = "x" // error: inconsistent constant types in enum declaration ) -
iotadefaults toint, matching Go's existing behavior. -
Explicit type conversions are allowed for cases where a different underlying type is desired, e.g.:
type SmallEnum const ( A = int8(1) B = int8(2) ) // underlying type: int8In this case all expressions must convert to (or default to) the same type, following normal Go conversion/typing rules.
-
The first expression establishes the type if there is any ambiguity; subsequent expressions are checked for consistency against it. This mirrors how
iota-based blocks already behave when only the first entry carries an explicit type in Go.
Examples
Integer enum (most common case)
type Color const (
Red = iota
Green
Blue
)
Equivalent Go:
type Color int
const (
Red Color = iota
Green
Blue
)
String enum
type LogLevel const (
Debug = "debug"
Info = "info"
Warn = "warn"
Error = "error"
)
Equivalent Go:
type LogLevel string
const (
Debug LogLevel = "debug"
Info LogLevel = "info"
Warn LogLevel = "warn"
Error LogLevel = "error"
)
Boolean-backed enum
type Toggle const (
Off = false
On = true
)
Equivalent Go:
type Toggle bool
const (
Off Toggle = false
On Toggle = true
)
Bit-flag enum with explicit expressions
type Permission const (
Read = 1 << iota
Write
Execute
)
Equivalent Go:
type Permission int
const (
Read Permission = 1 << iota
Write
Execute
)
Compatibility
- This is purely additive syntax sugar at the source level. After XGo's
compiler expands
type XXX const (...)into the equivalent Gotype+constdeclarations, the resulting Go AST is indistinguishable from hand-written Go code. - All downstream tooling (type-checking,
go vet, reflection, JSON marshaling,String()/Stringercodegen viago generate, etc.) works unchanged, since the generated type and constants are ordinary Go declarations. - Existing Go code using the traditional two-block form remains fully valid and is unaffected.