Skip to content

API Reference

import "github.com/tylergannon/polytype"

func Camel(name string) string

Camel converts a Go type name to lower camelCase for use as a discriminator value.

func Pascal(name string) string

Pascal returns a Go type name unchanged. It is the default discriminator inflection and preserves the historical concrete-type-name wire value.

func Snake(name string) string

Snake converts a Go type name to lower snake_case for use as a discriminator value.

Configuration is the common value accepted by polytype generators. Declarations, sealed-union settings, and Compose results implement it.

type Configuration interface {
// contains filtered or unexported methods
}

func Compose(configs ...Configuration) Configuration

Compose combines declarations and union settings into one configuration for a generator program, such as one calling codegen.Gen. A declaration file lists each Declare and SealedUnion as its own var _ = declaration instead; the polytype CLI rejects a Compose call there.

func DeclareType(typ TypeSpec) Configuration

DeclareType selects a named type by source identity without importing its application package into the generator binary. The generator resolves the name against current source and reports missing or unsupported types there. It has no schema entrypoint or field/provider rules; select outputs explicitly.

ConfigurationSpec is the resolved meaning of one or more configuration values. Generators obtain it with ResolveConfiguration.

type ConfigurationSpec struct {
Declarations []DeclarationSpec
SealedUnions []SealedUnionSpec
// contains filtered or unexported fields
}

func ResolveConfiguration(configs ...Configuration) (ConfigurationSpec, error)

ResolveConfiguration returns the complete meaning of configs and validates identities that must agree before source loading begins.

Declaration is an executable description of one code-generation root. It is both the value used by build-tagged declaration files and the value accepted by the programmatic code-generation API.

type Declaration[T any] struct {
// contains filtered or unexported fields
}

func Declare[T any](entrypoint ...func(T) json.RawMessage) *Declaration[T]

Declare selects T as a generation root. With no argument it does not imply JSON Schema or an accessor method. Passing a method expression or free function records its name for schema-accessor generation.

Both forms produce an ordinary configuration value:

config := polytype.Declare[Person]()
config := polytype.Declare(Person.Schema)

A declaration file read by the polytype CLI accepts both forms too: Declare[Person]() gives Person the generated Go JSON codecs, and TypeScript with --typescript, but no schema file or accessor.

func (d *Declaration[T]) Accessor[F any](field FieldRef[F], provider func(T) json.Marshaler) *Declaration[T]

Accessor registers a provider for field that is a struct method taking only the receiver T (equivalent to WithStructAccessorMethod).

func (d *Declaration[T]) Function[F any](field FieldRef[F], provider func(F) json.Marshaler) *Declaration[T]

Function registers a provider for field that is a free function taking only the field’s own value F (equivalent to WithFunction). field and provider must agree on F: passing a field of one type alongside a provider expecting another fails to compile.

func (d *Declaration[T]) Method[F any](field FieldRef[F], provider func(T, F) json.Marshaler) *Declaration[T]

Method registers a provider for field that is a struct method also taking the field’s own value F (equivalent to WithStructFunctionMethod). field and provider must agree on F: passing a field of one type alongside a provider expecting another fails to compile.

func (d *Declaration[T]) Ref() *Declaration[T]

Ref requests that, wherever T is referenced from another registered schema, it be rendered as a “$ref” into that schema’s “$defs” instead of being inlined (equivalent to AsRef).

func (d *Declaration[T]) RenderProviders() *Declaration[T]

RenderProviders requests generation of RenderedSchema() and provider execution at runtime (equivalent to WithRenderProviders).

func (d *Declaration[T]) StringerEnum[F any](field FieldRef[F]) *Declaration[T]

StringerEnum emits an integer enum field using its constant names instead of its underlying integer values (equivalent to WithStringerEnum).

DeclarationSpec is the generator-facing form of a Declaration.

type DeclarationSpec struct {
Type TypeSpec
EntrypointName string
EntrypointFunc bool
Rules []RuleSpec
}

FieldRef retains a field’s identity and value type after a declaration is evaluated. The value type keeps provider bindings type-safe.

type FieldRef[F any] struct {
// contains filtered or unexported fields
}

func Field[T, F any](name string) FieldRef[F]

Field returns a stable reference to a field of T.

FieldSpec identifies a field by owner and Go field name.

type FieldSpec struct {
Owner TypeSpec
Name string
}

Nullable represents a required object property whose value may be null. The zero value encodes as null; Present reports whether Value is non-null.

type Nullable[T any] struct {
Present bool
Value T
}

func (Nullable[T]) IsZero() bool

IsZero always reports false so json:“,omitzero” cannot omit a required nullable property.

func (n Nullable[T]) MarshalJSON() ([]byte, error)

MarshalJSON encodes null or a present non-null value.

func (n *Nullable[T]) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes null or a present value without mutating the receiver when decoding fails.

Optional represents an object property that may be absent. The zero value is absent; a present value may contain T’s zero value but may not encode as null. Containing struct fields must use json:“,omitzero” so absent values are omitted before MarshalJSON is called.

type Optional[T any] struct {
Present bool
Value T
}

func (o Optional[T]) IsZero() bool

IsZero reports whether the property is absent.

func (o Optional[T]) MarshalJSON() ([]byte, error)

MarshalJSON encodes a present non-null value.

func (o *Optional[T]) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a present non-null value without mutating the receiver when decoding fails.

RuleKind identifies one declaration rule.

type RuleKind string

const (
RuleAccessor RuleKind = "accessor"
RuleMethod RuleKind = "method"
RuleFunction RuleKind = "function"
RuleStringerEnum RuleKind = "stringer-enum"
RuleRef RuleKind = "ref"
RuleRenderProviders RuleKind = "render-providers"
)

RuleSpec is one executable declaration rule.

type RuleSpec struct {
Kind RuleKind
Field FieldSpec
ProviderName string
ProviderIsMethod bool
}

type SchemaFunction func() json.RawMessage

type SchemaMarker struct{}

func NewJSONSchemaBuilder[T any](SchemaFunction) SchemaMarker

NewJSONSchemaBuilder registers a function as being a stub that should be implemented with a proper json schema and, as needed, unmarshaler functionality.

func NewJSONSchemaFunc[T any](f SchemaMethod[T], _ ...SchemaMethodOption) SchemaMarker

NewJSONSchemaFunc registers a free function that takes the receiver as its sole parameter as a schema entrypoint. It is equivalent to NewJSONSchemaMethod.

Deprecated: use Declare(fn) with a free function instead.

func NewJSONSchemaMethod[T any](SchemaMethod[T], ...SchemaMethodOption) SchemaMarker

NewJSONSchemaMethod registers a struct method as a stub that will be implemented with a proper json schema and, as needed, unmarshaler functionality.

Deprecated: use Declare(T.Schema) instead. For example, NewJSONSchemaMethod(Task.Schema, WithStringerEnum(Task{}.Level)) becomes Declare(Task.Schema).StringerEnum(Field[Task, Level](“Level”)).

type SchemaMethod[T any] func(T) json.RawMessage

type SchemaMethodOption interface {
// contains filtered or unexported methods
}

func AsRef() SchemaMethodOption

AsRef requests that, wherever this type is referenced from another registered schema, it be rendered as a “$ref” into that schema’s “$defs” instead of being inlined.

Deprecated: use Declare(T.Schema).Ref() instead.

func WithFunction[T any](val T, f func(T) json.Marshaler) SchemaMethodOption

Deprecated: use Declare(T.Schema).Function(Field[T, F](“Field”), fn) instead.

func WithRenderProviders() SchemaMethodOption

WithRenderProviders requests generation of RenderedSchema() and provider execution at runtime.

Deprecated: use Declare(T.Schema).RenderProviders() instead.

func WithStringerEnum[T any](field T) SchemaMethodOption

Enum options (v1) - stubs for scanning/type-checking; parsed by scanner

Deprecated: use Declare(T.Schema).StringerEnum(Field[T, F](“Field”)) instead.

func WithStructAccessorMethod[T, U any](val T, f func(U) json.Marshaler) SchemaMethodOption

Deprecated: use Declare(T.Schema).Accessor(Field[T, F](“Field”), T.method) instead.

func WithStructFunctionMethod[T, U any](val U, f func(T, U) json.Marshaler) SchemaMethodOption

Deprecated: use Declare(T.Schema).Method(Field[T, F](“Field”), T.method) instead.

type SchemaMethodOptionObj struct{}

SealedUnionMarker is executable configuration for one sealed interface.

type SealedUnionMarker struct {
// contains filtered or unexported fields
}

func SealedUnion[I any](discriminator string, inflectors ...func(string) string) SealedUnionMarker

SealedUnion declares the discriminator property for the sealed interface I. A sealed interface is one whose own body declares an unexported method; its variants are inferred from the same-package struct types that declare that method directly, so membership needs no declaration. The default property is “type” and needs no declaration; SealedUnion sets a different property for every use of I in every generated schema, codec, and TypeScript output. The optional inflector determines each variant’s value from its concrete Go type name. Pascal is the default; Snake and Camel are also provided. Executable codegen configuration may supply any non-nil func(string) string. Build-tagged source markers accept the named Pascal, Snake, and Camel functions.

The declaration must appear in the build-tagged file of the package that declares I, exactly once per interface, with a string literal argument:

var _ = polytype.SealedUnion[Animal]("kind", polytype.Snake)

A declaration in another package, a duplicate declaration, a declaration for a non-sealed interface or a non-interface type, a non-literal argument, or an invalid property name is a generation error naming the interface.

SealedUnionSpec configures the discriminator property of one inferred sealed interface.

type SealedUnionSpec struct {
Type TypeSpec
Discriminator string
Inflect func(string) string
}

TypeSpec identifies a named Go type without retaining a runtime value.

type TypeSpec struct {
PackagePath string
Name string
Pointer bool
}
import "github.com/tylergannon/polytype/codegen"

Package codegen runs polytype generation from executable configuration values. It does not require a schema.go registration file in the target package.

func Gen(config polytype.Configuration, options ...Option) error

Gen runs generation with functional options. With no options, Declare(T.Schema) preserves the traditional schema-and-accessor behavior. A declaration without an entrypoint must select at least one output.

func Generate(config polytype.Configuration, opts Options) error

Generate runs the selected backends from the same declaration value used by build-tagged bindings.

DevalueOptions selects generated devalue Go codecs.

type DevalueOptions struct {
File string
PackageName string
ImportPath string
}

Option modifies the convenience Gen request.

type Option func(*Options)

func Devalue(file, packageName, importPath string) Option

Devalue writes strict devalue codecs to file.

func GoJSON() Option

GoJSON selects generated MarshalJSON and UnmarshalJSON support required by configured enums and sealed unions, without selecting JSON Schema.

func JSONSchema() Option

JSONSchema selects JSON Schema files. If the declaration carries an entrypoint, its Go accessor is emitted as well.

func Pretty() Option

Pretty formats JSON Schema output with indentation.

func Target(dir string) Option

Target loads and writes the configured package at dir.

func TypeScript(dir string, barrel ...bool) Option

TypeScript writes structural TypeScript declarations to dir.

func Validation() Option

Validation selects JSON Schema, its Go accessors, and generated validators.

func WithSession(session *goload.Session) Option

WithSession shares compatible Go package analysis within an invocation.

Options selects outputs for Generate. JSON Schema is optional: TypeScript, devalue, and generated Go JSON codecs can be requested independently.

type Options struct {
// Session shares Go analysis on persistent-cache misses. It does not affect
// cache identity, and a persistent hit performs no session work.
Session *goload.Session
TargetDir string
Pretty bool
Force bool
JSONSchema bool
GoCode bool
Validate bool
TypeScript *TypeScriptOptions
Devalue *DevalueOptions
// contains filtered or unexported fields
}

TypeScriptOptions selects structural TypeScript output.

type TypeScriptOptions struct {
Dir string
Barrel bool
}

Worker retains analysis and emitted TypeScript between serialized development edits. Its zero value is ready to use. Each Worker retains one request; use separate workers for independent targets. Close releases private source files.

Callback-free TypeScript requests reuse captured local source and type analysis. The first analysis checks the selected local source closure together so later edits retain a coherent graph. The owned session retains at most four graph versions. If Options.Session is supplied, its owner must call SetMaxUniverses with a positive limit for sustained edits; Worker does not change a shared session’s retention policy. This bounds graph versions, not bytes. Other output modes, workspaces, mutable native/embed dependencies and custom loader configurations keep Generate’s behavior. Versioned module-cache files and the Go toolchain must remain immutable during its lifetime. Changing environment, module controls, imports or file membership refreshes discovery. A failed edit never replaces the last successful output.

type Worker struct {
// contains filtered or unexported fields
}

func (w *Worker) Close() error

Close releases retained analysis and private files. The Worker may be reused.

func (w *Worker) Generate(config polytype.Configuration, opts Options) error

Generate updates the selected outputs from current authored source. Missing or modified output is repaired even when no input changed.

func (w *Worker) Stats() WorkerStats

Stats returns cumulative work for this Worker, including across Close calls.

WorkerStats counts retained TypeScript work. Other output modes use Generate directly and do not contribute. Emissions count backend executions, including executions whose bytes match the existing output; Replays run no backend.

type WorkerStats struct{ Analyses, Emissions, Replays uint64 }
import "github.com/tylergannon/polytype/jsonschema"

type DataType string

const (
Object DataType = "object"
Number DataType = "number"
Integer DataType = "integer"
String DataType = "string"
Array DataType = "array"
Null DataType = "null"
Boolean DataType = "boolean"
)

JSONSchema is a struct for describing a JSON Schema. It is fairly limited, and you may have better luck using a third-party library. This is a copy from go-openai’s “jsonschema.Definition{}” struct, with the difference being that this one holds references to json.Marshaler, rather than to itself.

type JSONSchema struct {
// Type specifies the data type of the schema.
Type DataType `json:"type" yaml:"type"`
// Description is the description of the schema.
Description string `json:"description,omitempty" yaml:"description,omitempty"`
// Enum is used to restrict a value to a fixed set of values. It must be an
// array with at least one element, where each element is unique. You will
// probably only use this with strings.
Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"`
// Properties describes the properties of an object, if the schema type is
// Object.
Properties map[string]SchemaNode `json:"properties,omitempty" yaml:"properties,omitempty"`
// Required specifies which properties are required, if the schema type is
// Object.
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
// Items specifies which data type an array contains, if the schema type is
// Array.
Items SchemaNode `json:"items,omitempty" yaml:"items,omitempty"`
// AdditionalProperties is used to control the handling of properties in an
// object that are not explicitly defined in the properties section of the
// schema. example: additionalProperties: true additionalProperties: false
// additionalProperties: jsonschema.Definition{Type: jsonschema.String}
AdditionalProperties any `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"`
Definitions map[string]SchemaNode `json:"$defs,omitzero" yaml:"$defs,omitempty"`
Const any `json:"const,omitempty"` // Provide a const value
// Strict will make all properties required and set additionalProperties to
// false. Applies only if Type = "object".
Strict bool `json:"-" yaml:"-"`
}

func (s JSONSchema) MarshalJSON() ([]byte, error)

type JSONUnionType []*JSONSchema

func (j JSONUnionType) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

type ObjectSchema struct {
Properties []SchemaProperty
Strict bool
Required []string
Description string
AdditionalProperties any
}

func (s *ObjectSchema) AddProperty(key string, value SchemaNode)

func (s *ObjectSchema) AddRequiredProperty(key string, value SchemaNode)

func (s ObjectSchema) MarshalJSON() ([]byte, error)

type ParentSchema struct {
*ObjectSchema
Definitions []SchemaProperty
// The key name for the definitions map. Defaults to "definitions"
DefinitionsKeyName string `json:"-"`
Title string `json:"title,omitzero"`
}

func (s *ParentSchema) AddDefinition(key string, value SchemaNode)

func (s ParentSchema) MarshalJSON() ([]byte, error)

type SchemaNode = json.Marshaler

func ArraySchema(items SchemaNode, description string) SchemaNode

func BoolSchema(description string) SchemaNode

func ConstSchema[T ~int | ~string](val T, description string) SchemaNode

ConstSchema returns a schema that accepts exactly val. Named values whose underlying type is int or string retain their corresponding JSON Schema type.

func EnumSchema[T ~int | ~string](description string, vals ...T) SchemaNode

EnumSchema returns a schema that accepts one of vals. Named values whose underlying type is int or string retain their corresponding JSON Schema type. If vals is empty, the returned node reports an error when marshaled.

func IntSchema(description string) SchemaNode

func RefSchemaEl(ref string) SchemaNode

A ref into definitions

func StringSchema(description string) SchemaNode

func UnionSchemaEl(alts ...SchemaNode) SchemaNode

An anyOf element

type SchemaProperty struct {
Key string
Value SchemaNode
}
import "github.com/tylergannon/polytype/grammar"

Package grammar is the exported entry point for lowering Go types into the static type-definition grammar of github.com/tylergannon/polytype/typegrammar.

Load reads a Go package the way the polytype CLI does, and Lower turns caller-chosen root types into a validated definition graph plus one grammar node per root. Callers use it to write their own code-generation backend without going through the CLI or asking for a schema file. FromPackages accepts an existing go/packages snapshot without loading its source again.

Roots are matched structurally and by name, so a *types.Type obtained from the caller’s own packages.Load works. A named root does not need a Declare marker; whatever the CLI would refuse in a struct field (maps, channels, functions, unconfigured interfaces, presence wrappers) is refused here with the same wording.

LoadMode is the information FromPackages needs for each package whose source is lowered. Add packages.NeedDeps to obtain that information for imports, or select the necessary dependency packages as explicit packages.Load patterns.

const LoadMode = syntax.PackageLoadNeeds | packages.NeedImports

Package is a loaded Go package that roots can be lowered against.

type Package struct {
// contains filtered or unexported fields
}

func FromPackages(config *packages.Config, root *packages.Package, additional ...*packages.Package) (*Package, error)

FromPackages lowers from an already parsed and type-checked package snapshot. It preserves root’s type identity and uses its Imports graph plus additional packages to resolve source. It never invokes a loader or the Go command; a reached package without source produces an error identifying what is missing. Packages used only through built-in grammar support (such as time.Time) do not need source. Errors are checked on each reached package; errors in an unrelated dependency do not prevent lowering an otherwise valid root.

config must describe the load that produced the snapshot and set the jsonschema tag in BuildFlags or GOFLAGS (Config.Env, or the process environment when Env is nil). Unlike LoadWithConfig, this constructor cannot add a tag to an already loaded package. Call packages.Load with at least LoadMode, Tests: false, and a parser that retains comments (the default does). All packages must belong to the same type-checking operation, with identical build settings, module selection and overlays. packages.Package does not retain those settings: their correspondence is the caller’s responsibility. Generation follows Go’s flag precedence: an explicit -tags replaces GOFLAGS tags rather than combining with them.

The caller owns the snapshot and must keep its ASTs, FileSet, Types, TypesInfo, source files and overlay resources alive and unchanged until all Lower calls finish. FromPackages does not mutate them. Do not share a Package between concurrent Lower calls, or reuse a snapshot after editing source; load a fresh snapshot for the next generation. No state is retained globally.

Production declaration admission removes jsonschema from explicit build tags; if that was the only explicit tag, production falls back to GOFLAGS tags. The jsonschema tag is removed from that production fallback as well. String-mode enum adaptation also retains the existing on-disk inspection of production JSON methods, including files excluded by jsonschema. That check does not use overlays; keep the production source view available on disk.

func Load(dir string) (*Package, error)

Load loads the Go package at dir with the jsonschema build tag, the same way the CLI does, together with the packages it references.

func LoadWithConfig(config *packages.Config, pattern string) (*Package, error)

LoadWithConfig loads pattern using the caller’s module, environment, parser, and overlay configuration. The same configuration resolves every recursively discovered type dependency. It includes the jsonschema build tag alongside explicit caller tags, overriding GOFLAGS tags as Load and the CLI do, and adds the load modes required by grammar. The caller must keep any backing files or parser resources alive until all Lower calls finish. Config and its BuildFlags are not modified. Declaration admission uses the same snapshot and production-tag rules as FromPackages, including for files supplied solely through Overlay.

func LoadWithLoader(config *packages.Config, load goload.LoadFunc, pattern string) (*Package, error)

LoadWithLoader is LoadWithConfig with a caller-owned package loading strategy. The loader serves both the initial request and dependencies reached by Lower, using the same tagged configuration. It must return ordinary go/packages data whose source, type information and positions describe that configuration.

func LoadWithSession(config *packages.Config, session *goload.Session, pattern string) (*Package, error)

LoadWithSession is LoadWithConfig with invocation-scoped package reuse. The session also serves recursively reached source dependencies.

func (p *Package) Lower(roots []Root) (typegrammar.Definitions, []typegrammar.Type, error)

Lower returns the validated definition graph reachable from the roots, and one grammar node per root in order. Named roots need no Declare marker.

func (p *Package) Types() *types.Package

Types is the package’s type-checked scope, for looking up the roots to lower.

Root is one shape a caller wants lowered. Type may be anonymous; Position is reported in diagnostics because an anonymous type has no source location of its own.

type Root struct {
Type types.Type
Position token.Position
}
import "github.com/tylergannon/polytype/typegrammar"

Package typegrammar defines the accepted, resolved static type-definition grammar shared by code-generation backends. Validate is its admission boundary.

A definition describes a Go type’s JSON value structure, retaining numeric kinds, pointer/value identity, collection shape, and field-local registrations. Source loading must resolve aliases, embedding/field selection, registrations, and JSON names before constructing this model. The builder’s TypeDefinitions adapter produces this model for the TypeScript backend.

Named definitions may reference one another, including recursive and mutually recursive references through Ref edges. Inline constructor nodes still form a finite DAG: a back-edge is valid only when it passes through a named definition. Nonproductive alias loops (Ref-only cycles) are rejected. Objects are closed, ordered sets of properties. Ordinary values are non-null; absence and null are separate, direct-field constructors. Unions are field-only constructors with explicit, resolved tags, including singleton unions. There is no general anyOf, any, or map constructor. A field whose wire shape is supplied outside the grammar (a runtime schema provider or an explicit schema reference) is the Provided form: it names the field and carries no shape, so a backend must either honor the supplied schema or refuse the field by name.

This is the static structural subset of the v1 contract, not a Go-source parser, arbitrary JSON Schema grammar, or claim of codec conformance. Unresolved external types and unproved custom wire mappings must be diagnosed by lowering, not replaced with a permissive node. Backends must define their projection explicitly: for example TypeScript’s number cannot enforce all Go ranges, and its object types are not validators.

New node kinds may be added in minor versions. Consumers must not treat a type switch over the node types as exhaustive: always provide a default case that reports an unrecognized kind rather than silently ignoring it.

Array retains the Go length, including zero. It does not by itself claim that the existing JSON Schema renderer enforces that length.

type Array struct {
Length int64
Element Type
}

type Definition struct {
Name Name
Type Type
Description string
Source token.Position
}

Definitions is the complete local definition graph, in source order. Every reference and union implementation must resolve here, including dependencies from supported packages. Consumers select output roots separately.

type Definitions []Definition

func (defs Definitions) Validate() error

Validate admits exactly the constructors and compositions documented by this package. It checks all definitions, including unused ones. It neither mutates nor normalizes the graph, executes user code, nor validates runtime JSON.

Named recursion through Ref edges and union implementations is admitted: a definition may reference itself or a mutually-dependent definition provided the cycle passes through at least one productive constructor (Object, Slice, Array, Pointer). Nonproductive alias loops where every edge is a Ref are rejected. Literal cycles of constructor pointers (hand-built graphs where a node’s child pointer points back to itself) remain rejected. Sharing is permitted and checked once per relevant context.

Example

package main
import (
"fmt"
g "github.com/tylergannon/polytype/typegrammar"
)
func main() {
payload := g.Name{PackagePath: "example.com/events", Name: "Created"}
defs := g.Definitions{
{Name: payload, Type: &g.Object{Fields: []g.Field{{
GoName: "Text", JSONName: "text", Value: &g.Required{Type: &g.Scalar{Kind: g.String}},
}}}},
{Name: g.Name{PackagePath: "example.com/events", Name: "Envelope"}, Type: &g.Object{Fields: []g.Field{{
GoName: "Event", JSONName: "event", Value: &g.Union{
Interface: g.Name{PackagePath: "example.com/events", Name: "Event"},
Discriminator: "kind",
Variants: []g.Variant{{Implementation: payload, Pointer: true, Tag: "created"}},
},
}}}},
}
fmt.Println(defs.Validate())
}
<nil>

func (defs Definitions) ValidateWithRoots(roots []Type) error

ValidateWithRoots admits the definitions and, in addition, each root node. A root is a node a caller asked to have lowered directly; no definition reaches it, so Validate alone would never see it and a shape this grammar excludes could be returned unchecked.

Roots are checked as anonymous operands, because that is what they are: they have no named object owner, so the compositions that require one (a string-mode enum, most obviously) are refused in a root exactly as they are in an anonymous position inside a definition.

Enum is a resolved registration, not a global adapter for GoType. The same Go type can have different mappings in different fields. Values retain exact Go constants, including integers not exactly representable by JavaScript number. Representability in a target language is a later backend obligation.

type Enum struct {
GoType Name
Kind ScalarKind
Mode EnumMode
Members []EnumMember
}

type EnumMember struct {
Name string
Value constant.Value
// Description is the constant's doc comment, for backends that document
// members (JSON Schema folds it into the enum's description).
Description string
}

type EnumMode uint8

const (
// EnumValues uses underlying string or integer constant values on the wire.
EnumValues EnumMode = iota
// EnumNames uses Go constant names as wire strings, for integer enums only.
// It is admitted only in direct fields of named object owners (Required,
// Optional or Nullable), including references to reusable enum definitions.
// Pointer/collection operands and anonymous owners cannot request this
// adaptation. String() methods never determine wire values.
EnumNames
)

Error identifies the first invalid constructor in deterministic definition, field, and variant order. Source is the nearest available source location; Path is usable even for programmatically constructed definitions without one.

type Error struct {
Path string
Source token.Position
Message string
}

func (e *Error) Error() string

type Field struct {
GoName string
JSONName string
Value FieldValue
Description string
Source token.Position
}

FieldValue is a closed sum of direct-field forms. In particular a Union is not a Type: refs, pointers, aliases and ordinary collections cannot hide one. A collection of named objects whose own fields contain unions is permitted.

type FieldValue interface {
// contains filtered or unexported methods
}

Name identifies a resolved Go type. PackagePath is always a full import path; pointer qualification and field-specific wire identity do not belong here.

type Name struct {
PackagePath string
Name string
}

func (n Name) String() string

Nullable is a required key that admits null or a value. Its admitted operands are scalars/enums, objects, pointers to objects, and refs to those shapes; time.Time is also supported; collection and union operands are excluded.

type Nullable struct{ Type Type }

Object’s properties retain resolved Go field order and are closed to unknown JSON properties. An empty object is valid. Embedding is resolved before here.

type Object struct{ Fields []Field }

Optional is absent or a non-null value, corresponding to direct Optional[T]. Lowering must check the required json:“,omitzero” tag. It cannot occur inside collections or definitions because it does not implement Type.

type Optional struct{ Type Type }

OptionalUnion and UnionSlice are the only other admitted interface forms: direct Optional[I] and direct one-dimensional []I. Neither is a Type.

type OptionalUnion struct{ Union Union }

Pointer retains source indirection without introducing null into the grammar.

type Pointer struct{ Element Type }

Provided is a direct field whose JSON Schema is supplied outside the static grammar: by a runtime schema provider registered on the owning declaration (Ref is empty) or by an explicit reference in the field’s jsonschema tag (Ref is the reference text). It carries no Type, because the Go field’s static shape is not the wire contract. Optional reports Optional[T] with json:“,omitzero”; a Nullable wrapper is refused by lowering. Backends that cannot honor a supplied schema, such as TypeScript and devalue, refuse the field by name.

type Provided struct {
Ref string
Optional bool
}

Ref is a resolved named-type edge, not an arbitrary URI or a request for a particular backend’s reference syntax (such as JSON Schema’s AsRef option).

type Ref struct{ Target Name }

Required is a required non-null field. Direct pointers and ordinary omitempty/omitzero tags are rejected during lowering; authors must state nullability or omission with Nullable or Optional.

type Required struct{ Type Type }

type Scalar struct{ Kind ScalarKind }

ScalarKind retains Go numeric width and signedness instead of prematurely projecting all JSON numbers to float64. Int and Uint remain platform-sized. byte/rune aliases normalize to Uint8/Int32. Complex and uintptr are excluded.

type ScalarKind string

const (
Bool ScalarKind = "bool"
String ScalarKind = "string"
Int ScalarKind = "int"
Int8 ScalarKind = "int8"
Int16 ScalarKind = "int16"
Int32 ScalarKind = "int32"
Int64 ScalarKind = "int64"
Uint ScalarKind = "uint"
Uint8 ScalarKind = "uint8"
Uint16 ScalarKind = "uint16"
Uint32 ScalarKind = "uint32"
Uint64 ScalarKind = "uint64"
Float32 ScalarKind = "float32"
Float64 ScalarKind = "float64"
)

Slice represents an ordinary non-null slice. Byte-like slices, whose default Go JSON representation is base64, are outside this static portable grammar.

type Slice struct{ Element Type }

Time is the renderer-owned time.Time leaf. Its wire type is string; neither date-time schema validation nor JavaScript Date precision is implied.

type Time struct{}

Type is the closed set of ordinary, non-null type constructors. Only pointer nodes implement it, allowing validation to detect cycles in hand-built graphs as well as cycles through named references. A node is admitted only after the entire Definitions value passes Validate; Go structs alone cannot enforce graph or context invariants. Do not mutate admitted graphs during consumption.

type Type interface {
// contains filtered or unexported methods
}

Union describes a direct registered interface field I. Its identity is the containing definition/field, not Interface alone. Tags have already been resolved from either explicit strings (including empty strings) or legacy implementation names. Discriminator is the effective, nonempty property name after applying registration defaults; whitespace is never trimmed here. Every member is a non-null object carrying the required Discriminator key with its exact Tag. A pointer registration does not imply nullable payloads.

type Union struct {
Interface Name
Discriminator string
Variants []Variant
// Source is the interface declaration's position, for diagnostics that
// name the union rather than one of its variants.
Source token.Position
}

type UnionSlice struct{ Union Union }

type Variant struct {
Implementation Name
Pointer bool
Tag string
Source token.Position
}
import "github.com/tylergannon/polytype/typescript"

Package typescript projects the validated Go type grammar into TypeScript structural type declarations.

The input is a validated github.com/tylergannon/polytype/typegrammar definition graph, as produced by github.com/tylergannon/polytype/grammar or by the CLI’s own lowering. The output is a types.ts module declaring one exported type alias per definition, and optionally an index.ts type-only barrel. The declarations are structural: there is no runtime decoder or validator, and consumers must validate untrusted data themselves.

The CLI’s --typescript flag and a Go program calling Generate on the same definitions produce the same bytes. A generator that already knows its roots therefore needs no Declare marker and no //go:build jsonschema file: it lowers them with grammar.Load and (*grammar.Package).Lower and hands the definitions to Generate. Every root a caller wants named in the output must be a definition; an anonymous root has no alias of its own.

Emitted identifiers are collision-safe and may differ from the Go type name, so a caller writing its own imports reads them from Result.Names rather than assuming the Go name.

GeneratedHeader identifies files owned by this generator.

const GeneratedHeader = "// Code generated by polytype. DO NOT EDIT.\n"

File is one generated output, named relative to whatever directory the caller writes it to.

type File struct {
Name string
Content []byte
}

Options selects the optional outputs.

type Options struct {
// Barrel adds an index.ts that re-exports every declared type, type-only.
Barrel bool
}

Result is the output of one Generate call.

type Result struct {
// Files holds types.ts first, then index.ts when Options.Barrel is set.
Files []File
// Names maps every definition to the identifier its alias was emitted
// under. The identifier is the Go type name unless that name is not a
// legal TypeScript identifier, is reserved, or is claimed by a definition
// of the same name in another package.
Names map[typegrammar.Name]string
}

func Generate(defs typegrammar.Definitions, options Options) (Result, error)

Generate validates defs and renders all declarations before returning any files. It never mutates defs.

import "github.com/tylergannon/polytype/devalue/codegen"

Package codegen emits Go encoders and strict decoders that move values between Go types and the devalue value model of github.com/tylergannon/devalue/v5.

The input is a validated github.com/tylergannon/polytype/typegrammar definition graph plus the caller’s root nodes, as produced by github.com/tylergannon/polytype/grammar. The output is one Go source file holding, for every definition and every root, an encoder, a strict decoder and a Stringify/Parse convenience wrapper. The file is written for a package other than the one declaring the Go types, so the emitted functions use only exported fields.

Every Go numeric kind is a JavaScript number: the encoder converts to float64 itself, so integers beyond 2^53 lose precision on the wire, whether they arrive as a scalar field or as an integer enum member. There is no BigInt. Enum members that share one underlying value are admitted: they share one wire value, which decodes to the first member declaring it. time.Time is the same string encoding/json produces, never a JavaScript Date. A required nil slice encodes as an empty array; a nil pointer where a value is required is an encode error. An absent Optional is no property at all; an absent Nullable is null. Unions encode as one object whose discriminator property holds the concrete type name.

Decoders are strict. They reject a missing required property, an unknown property, a value of the wrong kind, undefined, null where the field is not Nullable, an array whose length does not match a Go array’s, an enum non-member, an unknown union tag, and every devalue tagged form (Date, Map, Set, BigInt, RegExp, ArrayBuffer, boxed primitives), since the grammar admits none of them. Every diagnostic names a JSON-pointer-style path.

Encoders never dedupe: a fresh object and slice is built per value. Reducers and revivers are a caller concern; pass them to devalue.StringifyWith and devalue.Parse together with the generated encoder or decoder.

An anonymous struct type is supported as a definition’s own type and as the direct value of a Required, Optional or Nullable field — positions the emitted code reaches through field selectors on the parent value. Anywhere else — under a slice, an array or a pointer, or as a root — it is refused with a diagnostic naming the grammar path, because the emitter would have to declare a variable of that type: Go type identity includes struct tags, and the grammar does not carry them, so a spelled type would not be assignable.

func Generate(defs typegrammar.Definitions, roots []typegrammar.Type, opts Options) ([]byte, error)

Generate emits one Go source file holding, for each definition and each root, an encoder and a strict decoder as free functions over the type’s exported fields, plus Stringify and Parse wrappers. It never mutates defs.

Options configures the emitted file.

type Options struct {
// PackageName is the package clause of the emitted file.
PackageName string
// ImportPath is the import path of the package the file will live in.
// Types declared there are referenced unqualified, so the file never
// imports itself.
ImportPath string
}

Generated by gomarkdoc