Field validation
Field validation is the optional net between “type-safe in Go” and “template may still reference a field that does not exist”. Enable it and templator verifies, when a template is first loaded, that the fields it references exist in the data type.
Enable it
Section titled “Enable it”type ArticleData struct { Title string Content string}
reg, err := templator.NewRegistry[ArticleData]( fsys, templator.WithFieldValidation(ArticleData{}),)
_, err = reg.Get("article")You pass an instance of the data type (ArticleData{}). It is only used to
bind the type parameter — the registry derives the type from T; the value’s
contents are irrelevant. A zero value is conventional.
When it runs
Section titled “When it runs”Validation happens once per template, at first Get, before the content
is parsed. This order matters:
- A field mismatch fails the
Getcall with a*templator.ValidationError— typically at startup or on the first render of the template. - Templates are cached, so the cost is paid once, not per render.
- Templates that reference only valid fields behave exactly as without validation. There is no runtime overhead once a template is cached.
What it checks
Section titled “What it checks”Validation extracts field references from the template source and walks each
one against the reflected type of T (following nested structs and
dereferencing pointers). Three shapes of reference are extracted:
// 1. Whole-action field references, with optional whitespace:{{.Title}}{{ .Title }}
// 2. Nested field paths, dotted:{{.User.Name}}
// 3. Fields inside an if header:{{if .User.Admin}}...{{end}}For each reference, templator checks that the path resolves in the data
type. In the example above, ArticleData must have a Title field, and
User.Name must resolve through a struct (or pointer-to-struct) field
User with a field Name. A missing field produces:
template 'article' validation error: 'Author' - field 'Author' not found in type ArticleData*ValidationError carries the pieces for programmatic checks — the
Err field holds the per-field cause, as detailed in
Errors:
type ValidationError struct { TemplateName string FieldPath string Err error}The data type itself must be a struct (or a pointer to one). A registry
whose T is, say, a map or a slice is rejected at Get time with a
validation error like expected struct type, got map.
What it does not check — and where it is wrong
Section titled “What it does not check — and where it is wrong”The extractor matches the three patterns above and nothing else. These constructs are not checked at all:
{{range .Items}}and{{with .X}}(fields inside them are not extracted unless they also appear as one of the three covered shapes)- Pipelines such as
{{.Title | upper}} - Variables (
$x), computed expressions, and{{template "partial" .}}includes
Templates using only these constructs pass validation unhindered — silence is the worst case for them.
Three covered or adjacent shapes behave incorrectly today, confirmed against the implementation:
-
Method calls inside covered shapes are rejected.
{{.Footer}}whereFooteris a method (not a field) matches the pattern, but the check looks at struct fields only, soGetfails with a validation error for a template that renders fine:template 't' validation error: 'Footer' - 'field 'Footer' not found in type Data'If your templates call methods, do not enable
WithFieldValidationfor those registries. -
Dotted paths through non-struct fields currently panic. A path like
{{.Meta.Key}}whereMetais a map (or slice, or interface) passes the regex but the path walk callsFieldByNameon a non-struct type, crashingGetwithout the error return:panic: reflect: FieldByName of non-struct type map[string]stringKeep validated templates to plain nested structs and pointer chains.
-
Unexported fields pass validation but fail to render. A template referencing
{{.title}}(unexported) loads cleanly, then fails atExecute— a false negative within the covered shapes.
In short: for the safe subset — exported struct fields, nested structs,
pointer chains — validation is dependable; outside that subset it is
anything from silent to outright wrong. Drop WithFieldValidation for any
template that calls methods or walks into maps, and keep regression tests
exercising real renders (Test templates).