cast

Safe, predictable type conversion for Go — turn any value into the exact type you need without panics.

Library
Go
vv1.10.0
3,981stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
47/100Fair
Development Activity8
Maintenance20
Community60
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
71/100Good
Architecture80
Code Quality85
Innovation65
Learning Curve55

Cast is a small Go library for converting between value types safely and predictably. It exposes ToString, ToInt, ToBool, ToTime, and dozens of similar functions that accept any value — including untyped interface{} results from JSON, YAML, or TOML — and produce the target type, falling back to the zero value when the conversion is ambiguous. Each To___ function has a paired To___E variant that returns an explicit error instead of silently returning the zero value, so callers can opt into strict handling only where they need it.

Originally built for Hugo, where front matter arrives as loosely-typed data from YAML, TOML, or JSON sources, Cast is now used across the spf13 ecosystem and any Go codebase that needs to normalize dynamic, weakly-typed input into concrete types without hand-rolling type switches and strconv boilerplate.

What You Get

  • To___ conversion functions - ToString, ToInt, ToBool, ToFloat64, ToTime, ToDuration and more, each returning the target type’s zero value on failed conversion
  • To___E error-returning variants - identical conversions that return an explicit error instead of masking failures behind a zero value
  • Generic ToE[T]/To[T] entry points - a single generic function dispatches to the right typed converter for any Basic-constrained type parameter
  • Map and slice casting helpers - ToStringMapString, ToSlice, and related functions for converting collection types recursively
  • Alias resolution via reflection - resolveAlias() unwraps named types (e.g. type MyInt int) back to their underlying kind so conversions work on custom types too

Common Use Cases

  • Parsing YAML/TOML/JSON front matter - a static site generator like Hugo reads user-authored front matter as interface{} values and needs to coerce them into typed Go fields without crashing on unexpected input
  • Normalizing configuration values - a CLI or config library accepts config values from flags, env vars, and files in different native types and casts them all to the type the caller expects
  • Handling loosely-typed API responses - code unmarshaling JSON into map[string]interface{} needs to safely extract typed values (strings, numbers, times) from arbitrary keys
  • Building small CLI tools - accepting user input as strings and converting to numeric or boolean flags without repetitive strconv boilerplate and panic-prone type assertions

Under The Hood

Architecture Cast is organized as a single flat package with one file per conversion domain — basic.go (bool/string), number.go (generic integer/unsigned/float parsing via shared parseInt/parseUint/parseFloat helpers), time.go (delegating to an internal/ subpackage for date-format detection), map.go and slice.go (collection casting), alias.go (reflection-based resolution of named types back to basic kinds), and indirect.go (pointer dereferencing). cast.go sits on top as the generic entry point, dispatching ToE[T Basic]/To[T] to the correct typed function via a type switch on the zero value of T. The bulk of the public API (the non-generic ToInt, ToBool, ToTime, etc. and their panic-safe non-E counterparts) is not hand-written but generated into zz_generated.go by a small generator/ program built on dave/jennifer’s Go AST builder, driven by a go:generate directive — a deliberate choice that keeps dozens of near-identical wrapper functions from drifting out of sync as the E-suffixed implementations evolve.

Tech Stack The runtime dependency surface is effectively empty: go.mod declares Go 1.21+ (for generics) and pulls in only stdlib packages (encoding/json, html/template, reflect, regexp, strconv, time) at runtime; the sole non-indirect require, frankban/quicktest, is exercised only from _test.go files, with go-cmp, kr/pretty, kr/text, and go-internal following as its transitive test-only dependencies. Code generation is handled by dave/jennifer in the separate generator/ module. Development tooling is reproducible via devenv.nix/devenv.lock and direnv, with a justfile exposing test (go test -race -shuffle on), lint (golangci-lint with errcheck, govet, ineffassign, misspell, unused), and fmt (gofmt/goimports/gci). CI runs the test suite on GitHub Actions on every push/PR to master, plus a separate scheduled OpenSSF Scorecard workflow that publishes a supply-chain security score.

Code Quality Every source file has a matching test.go counterpart (basic, number, time, map, slice, alias, indirect, cast, plus an internal number_internal_test.go for unexported helpers), and the test command runs with both -race and -shuffle on to catch ordering and concurrency bugs. Errors are handled explicitly and consistently: nearly every exported function has a To___E variant that returns a wrapped error (fmt.Errorf with %w) rather than swallowing failures, while the plain To__ variants intentionally trade the error for a documented zero-value fallback. golangci-lint enforces errcheck, govet, ineffassign, misspell, and unused across the codebase, and gofmt/goimports/gci keep formatting and import grouping consistent. No CONTRIBUTING.md exists, but exported identifiers carry GoDoc comments throughout.

API Design Cast’s public surface follows one consistent idiom across every conversion: a To<Type> function that never errors (returning the type’s zero value on failure) paired with a To<Type>E function that returns the same value plus an explicit error — letting callers opt into strict handling only where they need it, without forcing error-checking boilerplate everywhere. On top of the roughly twenty concrete conversion functions, a newer generic layer (ToE[T Basic], To[T Basic], ToNumberE[T Number]) lets callers write one generic call site instead of picking the right named function, using Go’s type-constraint interfaces (Basic, Number, integer, unsigned, float) to keep the generic dispatch exhaustive. The zero-value-on-failure versus explicit-error twin pattern is a well-established Go idiom rather than a novel one, but the code-generation step that derives the non-E functions from the E functions is a pragmatic way to keep a large, repetitive API surface correct as it grows.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search