Type Registry
The type registry controls how Go types are converted to and from Risor types. It is used when passing Go values into scripts and when returning results. The registry is optional (Risor handles common types automatically), but it enables precise control over custom type conversions.
Creating a Registry
Use risor.NewTypeRegistry() to start building a registry, register your
conversions, and call Build() to finalize it.
registry := risor.NewTypeRegistry().
RegisterFromGo(reflect.TypeOf(User{}), func(v any) (object.Object, error) {
u := v.(User)
return object.NewMap(map[string]object.Object{
"id": object.NewInt(int64(u.ID)),
"name": object.NewString(u.Name),
}), nil
}).
Build()Then pass the registry when evaluating scripts:
result, err := risor.Eval(ctx, source,
risor.WithEnv(risor.Builtins()),
risor.WithTypeRegistry(registry),
)RegisterFromGo
RegisterFromGo registers a conversion function that transforms a Go value
into a Risor object.Object. The first argument is a reflect.Type that
identifies which Go type this conversion handles. The second argument is the
conversion function.
registry := risor.NewTypeRegistry().
RegisterFromGo(reflect.TypeOf(Order{}), func(v any) (object.Object, error) {
o := v.(Order)
return object.NewMap(map[string]object.Object{
"id": object.NewInt(int64(o.ID)),
"total": object.NewFloat(o.Total),
}), nil
}).
Build()When a Go value of the registered type is passed into a script environment, the registry's conversion function is called automatically.
GoFunc
GoFunc wraps an arbitrary Go function for use in Risor. The function's
parameters and return values are converted automatically using reflection and
the type registry.
env := risor.Builtins()
env["add"] = func(a, b int) int { return a + b }
result, err := risor.Eval(ctx, `add(3, 4)`, risor.WithEnv(env))
// result: 7When a Go function is placed in the environment, Risor wraps it as a GoFunc
internally. The type registry is consulted for any parameter or return types
that need conversion beyond the built-in defaults.
GoStruct
GoStruct wraps a Go struct pointer and exposes its exported fields and
methods to Risor scripts. Fields are accessible by name and methods can be
called directly.
type Service struct {
Name string
Version int
}
func (s *Service) Info() string {
return fmt.Sprintf("%s v%d", s.Name, s.Version)
}
svc := &Service{Name: "MyApp", Version: 2}
result, err := risor.Eval(ctx, `svc.Info()`,
risor.WithEnv(map[string]any{"svc": svc}),
)
// result: "MyApp v2"Struct field access in scripts uses the exported Go field name:
svc.Name // "MyApp"
svc.Version // 2
svc.Info() // "MyApp v2"How Conversion Works
Both GoFunc and GoStruct rely on the type registry to convert values
between Go and Risor types. The conversion flow is:
Go to Risor. When a Go value enters the script environment, the registry checks for a registered
FromGoconversion. If none is found, default conversions are applied (strings, numbers, bools, slices, maps). Struct pointers are wrapped asGoStructand functions asGoFunc.Risor to Go. When a script calls a Go function or sets a struct field, the Risor object is converted back to the expected Go type using the registry or default conversions.
Custom registry conversions take priority over the defaults, giving you full control over how your domain types appear in scripts.