A powerful Go library for flexible type conversion, validation, and dynamic function calling. Particularly useful when working with JSON, APIs, or any scenario requiring robust type handling.
go get github.com/KarpelesLab/typutil- Type Conversion: Intelligent conversion between types with
AssignandAs - Validation: Struct field validation with custom validators
- Dynamic Functions: Transform any function into a callable with automatic argument conversion
- Map to Struct: Convert
map[string]anyto structs with JSON tag support - Flexible Conversion: Handle strings, numbers, booleans, slices, maps, and custom types
Convert any value to a target type:
// Convert string to int
result, err := typutil.As[int]("42")
// result = 42
// Convert map to struct
type User struct {
Name string
Age int
}
m := map[string]any{"Name": "Alice", "Age": 30}
user, err := typutil.As[User](m)
// user = User{Name: "Alice", Age: 30}Assign values with automatic conversion:
var age int
err := typutil.Assign(&age, "42")
// age = 42
var user User
m := map[string]any{"Name": "Bob", "Age": "25"}
err := typutil.Assign(&user, m)
// user = User{Name: "Bob", Age: 25}The As function provides type-safe conversion from any value to a target type T.
// Basic types
i, _ := typutil.As[int]("123") // 123
f, _ := typutil.As[float64]("3.14") // 3.14
s, _ := typutil.As[string](42) // "42"
b, _ := typutil.As[bool](1) // true
// Struct conversion
type Person struct {
Name string
Age int
}
type Employee struct {
Name string
Age string
}
p := Person{Name: "Alice", Age: 30}
e, _ := typutil.As[Employee](p)
// e = Employee{Name: "Alice", Age: "30"}The Assign function assigns a value to a pointer with automatic conversion.
var result int
err := typutil.Assign(&result, "42")
var user User
err := typutil.Assign(&user, map[string]any{
"Name": "Charlie",
"Age": 35,
})Convert maps to structs with support for JSON tags:
type Config struct {
Host string
Port int
Timeout float64
Username string `json:"user"`
Password string `json:"pass"`
}
m := map[string]any{
"Host": "localhost",
"Port": "8080", // Converts string to int
"Timeout": 30.5,
"user": "admin", // Matches via json tag
"pass": "secret",
"Extra": "ignored", // Unknown fields are ignored
}
config, err := typutil.As[Config](m)- Primitives: String, Int, Float, Bool, Byte slices
- Pointers: Automatic wrapping/unwrapping
- Slices: Element-wise conversion
- Maps: Key/value conversion
- Structs: Field-by-field conversion with JSON tag support
- Custom Types: Via
AssignableToandvalueScannerinterfaces
Validators allow you to enforce constraints on struct fields during assignment.
type User struct {
Email string `validator:"not_empty"`
Password string `validator:"minlength=8,maxlength=64"`
Color string `validator:"hex6color"`
IP string `validator:"ip_address"`
}
m := map[string]any{
"Email": "user@example.com",
"Password": "secret123",
"Color": "#FF5733",
"IP": "192.168.1.1",
}
user, err := typutil.As[User](m)
// Validation runs automatically during conversionnot_empty- Ensures the value is not emptyminlength=N- Minimum string lengthmaxlength=N- Maximum string lengthip_address- Valid IP addresshex6color- Valid 6-character hex color (e.g., #FF5733)hex64- Valid 64-character hex string
Register validators with SetValidator:
func init() {
// Simple validator
typutil.SetValidator("required", func(s string) error {
if s == "" {
return errors.New("value is required")
}
return nil
})
// Validator with arguments
typutil.SetValidatorArgs("min", func(i int, minVal int) error {
if i < minVal {
return fmt.Errorf("value must be at least %d", minVal)
}
return nil
})
}
type Product struct {
Name string `validator:"required"`
Price int `validator:"min=0"`
}Apply multiple validators to a single field:
type Account struct {
Username string `validator:"not_empty,minlength=3,maxlength=20"`
Email string `validator:"not_empty,email"`
}Transform any function into a generic callable with automatic argument conversion.
func Add(a, b int) int {
return a + b
}
f := typutil.Func(Add)
res, err := typutil.Call[int](f, ctx, 1, "2")
// res = 3 (string "2" converted to int)func Add(a, b int) int {
return a + b
}
f := typutil.Func(Add).WithDefaults(typutil.Required, 42)
res, err := typutil.Call[int](f, ctx, 58)
// res = 100 (second argument defaults to 42)Functions can accept context.Context as the first parameter:
func ProcessData(ctx context.Context, data string) (string, error) {
// Check context cancellation
select {
case <-ctx.Done():
return "", ctx.Err()
default:
return strings.ToUpper(data), nil
}
}
f := typutil.Func(ProcessData)
result, err := typutil.Call[string](f, ctx, "hello")
// result = "HELLO"// AsString - Convert any type to string
str, ok := typutil.AsString(42) // "42", true
str, ok := typutil.AsString([]byte{65}) // "A", true
// AsInt - Convert to int64
num, ok := typutil.AsInt("42") // 42, true
num, ok := typutil.AsInt(3.14) // 3, true (rounds)
// AsFloat - Convert to float64
f, ok := typutil.AsFloat("3.14") // 3.14, true
// AsBool - Convert to bool
b := typutil.AsBool("yes") // true
b := typutil.AsBool(0) // false
b := typutil.AsBool("non-empty") // truetype Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 30}
var m map[string]any
err := typutil.Assign(&m, p)
// m = map[string]any{"Name": "Alice", "Age": 30}var rawData map[string]any
json.Unmarshal(jsonBytes, &rawData)
type Config struct {
Host string
Port int `json:"port"`
}
config, err := typutil.As[Config](rawData)type APIResponse struct {
Status string
Code int
Message string
}
responseMap := map[string]any{
"Status": "success",
"Code": "200", // String from JSON
"Message": "OK",
}
response, err := typutil.As[APIResponse](responseMap)type UserForm struct {
Username string `validator:"not_empty,minlength=3"`
Email string `validator:"not_empty"`
Age int `validator:"min=18"`
}
formData := map[string]any{
"Username": r.FormValue("username"),
"Email": r.FormValue("email"),
"Age": r.FormValue("age"), // String from form
}
user, err := typutil.As[UserForm](formData)
if err != nil {
// Validation or conversion failed
http.Error(w, err.Error(), http.StatusBadRequest)
return
}user, err := typutil.As[User](data)
if err != nil {
if errors.Is(err, typutil.ErrAssignImpossible) {
// Type conversion not possible
}
if errors.Is(err, typutil.ErrInvalidSource) {
// Source value is invalid (nil)
}
// Handle validation errors
}- Type conversion functions are cached for performance
- Validators are registered once at initialization
- Reflection is used internally but optimized with caching
- Zero allocations for simple type conversions
Contributions are welcome! Please feel free to submit issues or pull requests.
See LICENSE file for details.