Skip to content

Type as Arguments (based on reflect.Type)

xushiwei edited this page Jun 18, 2026 · 2 revisions

This proposal adds Type as Arguments syntax to XGo, allowing bare type expressions to be passed where a reflect.Type parameter is expected. The compiler automatically rewrites each type expression to reflect.TypeFor[T](), eliminating boilerplate without any runtime changes.


Motivation

Since Go 1.22, the idiomatic way to obtain a reflect.Type is reflect.TypeFor[T](). While cleaner than the old reflect.TypeOf((*T)(nil)).Elem() idiom, it still clutters call sites — especially with multiple types:

foo(reflect.TypeFor[int]())
accept(reflect.TypeFor[A](), reflect.TypeFor[B](), reflect.TypeFor[C]())

The type names are the only semantic content; the wrappers are pure ceremony. XGo should let developers express the same intent directly:

foo int
accept A, B, C

Proposal

Syntax

When a parameter is declared as reflect.Type or ...reflect.Type, callers may pass bare type expressions at that position:

// Single reflect.Type parameter
foo int
foo MyStruct
foo *http.Request

// Variadic ...reflect.Type parameter
accept A, B, C

Target Function Signatures

The feature activates when the callee declares a parameter of type reflect.Type (from the standard library):

// Single
func foo(t reflect.Type)

// Variadic
func accept(typs ...reflect.Type)

Detailed Design

Applicability Rules

  1. reflect.Type parameters only. The feature applies to parameters declared as reflect.Type. It does not apply to interface{}, any, or user-defined aliases of reflect.Type.

  2. Any valid type expression. Named types, pointer types, slice types, map types, generic instantiations, and so on are all accepted.

  3. Variadic expansion. For ...reflect.Type, a comma-separated list of type expressions is accepted; each is rewritten independently.

  4. Mixed argument lists. Non-reflect.Type parameters continue to accept ordinary value expressions unchanged.

Compiler Rewrite

Each bare type expression T at a reflect.Type argument position is lowered to:

reflect.TypeFor[T]()

The generated Go code is standard and requires no runtime changes.

Examples

XGo Generated Go
foo int foo(reflect.TypeFor[int]())
foo *http.Request foo(reflect.TypeFor[*http.Request]())
accept A, B, C accept(reflect.TypeFor[A](), reflect.TypeFor[B](), reflect.TypeFor[C]())
setHandler "events", MyEvent setHandler("events", reflect.TypeFor[MyEvent]())
schema.Add Table, Index, View schema.Add(reflect.TypeFor[Table](), reflect.TypeFor[Index](), reflect.TypeFor[View]())

Disambiguation

At a reflect.Type argument position, XGo first tries to resolve the argument as a type. If it resolves to a type in the current scope, it is rewritten to reflect.TypeFor[T](). Otherwise, it is treated as a value expression of type reflect.Type (e.g., a variable).

foo t         // t is a reflect.Type variable — passed as-is
foo int       // int is a type — rewritten to foo(reflect.TypeFor[int]())
foo MyStruct  // MyStruct is a type — rewritten to foo(reflect.TypeFor[MyStruct]())

Impact on Existing Code

This is a purely additive change. Existing calls using explicit reflect.TypeFor[T]() remain valid. The new syntax is only available in XGo source files (.xgo / .gox); generated Go files always use the explicit form.


Conclusion

Type as Arguments eliminates the most common source of verbosity in reflection-heavy Go code. The transformation is mechanical, the output is standard Go, and existing code is entirely unaffected.

Clone this wiki locally