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.

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.

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

type Options struct {
TargetDir string
Pretty bool
Force bool
JSONSchema bool
GoCode bool
Validate bool
TypeScript *TypeScriptOptions
Devalue *DevalueOptions
}

TypeScriptOptions selects structural TypeScript output.

type TypeScriptOptions struct {
Dir string
Barrel bool
}
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.

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.

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

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

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 (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"

Package devalue implements devalue’s two serialization forms for structured JavaScript values, feature-equivalent to the devalue release named by UpstreamVersion.

Stringify and Parse use the flat JSON-array transport format. Uneval and UnevalWith emit JavaScript expressions and preserve shared references and cycles. The value model is shared, but support is serializer-specific: typed arrays, DataView, URL, URLSearchParams and Temporal are available to Uneval and remain unsupported by the flat format. Generated typed codecs target the flat format. Go value types cannot preserve distinct JavaScript identity for equal Date, RegExp, URL, URLSearchParams, or Temporal values, or shared identity for zero-length slices. Go strings are UTF-8, so a JavaScript string holding an unpaired surrogate has no Go form.

UpstreamVersion is the release of the JavaScript devalue package (https://github.com/sveltejs/devalue\) that this package is feature-equivalent to. Each polytype release names exactly one: for every value the Go model can express and each serializer supports, Stringify and Uneval write the bytes that release writes, and Parse reads that release’s documents back into the same value. The package documentation lists what the Go model cannot express.

It is the devalue version pinned in the repository’s package.json and the version that recorded the conformance goldens, and a test fails if the three disagree. Moving it to a newer devalue is a deliberate change that re-records the goldens and ports every behavior the new release changed.

const UpstreamVersion = "5.9.4"

Hole is an empty slot in a sparse array. It appears in the []any produced by Parse wherever the array had no element, and may be used in a []any passed to Stringify to produce holes.

var Hole = HoleValue{}

Undefined is the JavaScript value `undefined`. It is what Parse returns for the payload “-1”, and what Stringify writes as the bare token “-1”.

var Undefined = UndefinedValue{}

func CompareUTF16(a, b string) int

CompareUTF16 compares two strings the way JavaScript’s relational operators and `Array#sort` do: lexicographically by UTF-16 code unit. It returns -1, 0 or +1.

This is not the same as Go’s `<`, which compares UTF-8 bytes. The two orders agree everywhere except when an astral character (U+10000 and above, encoded in UTF-16 as a surrogate pair D800–DFFF) is compared against a character in U+E000–U+FFFF: JavaScript puts the astral character first, Go puts it last. Any sort whose result reaches the wire — a remote function’s payload, which the client also computes — has to use this one.

func Parse(s string, revivers map[string]func(any) (any, error)) (any, error)

Parse parses devalue’s flat format. revivers maps a custom type tag to a function that turns the already-parsed payload into a value; revivers take precedence over the built-in tags.

Values come back as: nil for null, Undefined for undefined, bool, float64, string, []any (with Hole in empty slots), *Object, *Map, *Set, Date, BigInt, RegExp, ArrayBuffer and *Boxed.

func QuoteString(s string) string

QuoteString writes s as the JavaScript string literal devalue writes: a JSON string in which `<` becomes \u003C, so that nothing a value carries can close the <script> element the document puts it in.

It is Uneval of a string, exposed on its own because a document is also full of strings that are not values — a module URL, a cache key — and those have to be escaped by the same rule.

func SortStringsUTF16(s []string)

SortStringsUTF16 sorts a slice the way JavaScript’s `Array#sort` sorts an array of strings.

func Stringify(v any) (string, error)

Stringify serializes a value into devalue’s flat-array format.

func StringifyWith(v any, reducers []Reducer) (string, error)

StringifyWith is Stringify with custom reducers, which run before the built-in type handling.

func Uneval(v any) (string, error)

Uneval turns a value into the JavaScript expression that creates an equivalent value — devalue’s `uneval`, which is what SvelteKit’s SSR document embeds for hydration.

Values referenced more than once (and every cycle) are hoisted into an immediately-invoked function so that reference identity survives the round trip: `(function(a){a.self=a;return a}({}))`. A BigInt, or a string of at least 128 UTF-16 code units, that repeats is hoisted the same way when that is shorter, so the output grows linearly with the input.

A typed array or DataView is written with the whole of its Buffer, including bytes outside the view; see TypedArray.Subarray.

func UnevalWith(v any, replacer Replacer) (string, error)

UnevalWith is Uneval with a replacer hook.

ArrayBuffer is a JavaScript ArrayBuffer, serialized as base64.

type ArrayBuffer []byte

BigInt is a JavaScript BigInt, held as its decimal digits.

type BigInt string

Boxed is a boxed primitive — `Object(42)`, `new String(“x”)` — serialized as [“Object”, i]. Always use *Boxed so that repeated references share identity.

type Boxed struct {
Value any
}

func NewBoxed(v any) *Boxed

NewBoxed boxes a primitive.

DataView is a JavaScript DataView over an ArrayBuffer. As with TypedArray, Buffer is the whole buffer and the view’s extent is separate.

type DataView struct {
Buffer ArrayBuffer
ByteOffset int
ByteLength int
}

func NewDataView(buf ArrayBuffer) *DataView

NewDataView views the whole of buf.

func NewDataViewRange(buf ArrayBuffer, byteOffset, byteLength int) *DataView

NewDataViewRange views byteLength bytes of buf starting at byteOffset. As with TypedArray.Subarray, serializing it discloses all of buf.

Date is a JavaScript Date. It serializes as an ISO 8601 string with millisecond precision in UTC.

type Date time.Time

func (d Date) Time() time.Time

Time returns the underlying time.

DevalueError is the Go form of devalue’s DevalueError: a value devalue refuses to serialize, with the path at which it was found.

type DevalueError struct {
Message string
Path string
}

func (e *DevalueError) Error() string

HoleValue is the type of Hole.

type HoleValue struct{}

func (HoleValue) MarshalJSON() ([]byte, error)

MarshalJSON renders an array hole as null, as JSON.stringify does.

func (HoleValue) String() string

Map is a JavaScript Map: ordered entries, keys compared by identity the way SameValueZero compares them (primitives by value, containers by reference).

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

func NewMap(kv ...any) *Map

NewMap builds a Map from alternating key/value arguments.

func (m *Map) Entries() []MapEntry

Entries returns the entries in insertion order.

func (m *Map) Get(key any) (any, bool)

Get returns the value stored under key.

func (m *Map) Len() int

Len returns the number of entries.

func (m *Map) Set(key, value any)

Set adds or replaces an entry.

MapEntry is one key/value pair of a Map.

type MapEntry struct {
Key any
Value any
}

Object is a JavaScript plain object with an explicit property order.

Property order is part of the serialized bytes, so Stringify accepts both *Object (order preserved) and map[string]any (keys sorted, since a Go map has no order). Parse always produces *Object.

type Object struct {
// NullProto reports whether this is an `Object.create(null)` object,
// which devalue tags as ["null", key, value, ...].
NullProto bool
// contains filtered or unexported fields
}

func NewNullProtoObject(kv ...any) *Object

NewNullProtoObject is NewObject for a null-prototype object.

func NewObject(kv ...any) *Object

NewObject builds an object from alternating key/value pairs. It panics if the arguments are not pairs of (string, any).

func (o *Object) Get(key string) (any, bool)

Get returns the named property.

func (o *Object) Keys() []string

Keys returns the property names in insertion order.

func (o *Object) Len() int

Len returns the number of properties.

func (o *Object) MarshalJSON() ([]byte, error)

MarshalJSON renders the object the way JSON.stringify would, so a parsed devalue tree can be round-tripped through encoding/json into a typed Go value. Property order is preserved, and an `undefined` property is omitted exactly as JSON.stringify omits it.

func (o *Object) Set(key string, value any)

Set assigns a property, appending it if new and keeping its original position if it already exists.

A Reducer is a custom serializer, the Go form of devalue’s `reducers` argument. Fn reports ok=false when it does not apply to the value, in which case the next reducer (and finally the built-in handling) is tried. A reducer that applies emits [“<Key>”, i] where i indexes the replacement value.

Reducers are held in a slice rather than a map because they are tried in order and the order is observable in the output.

type Reducer struct {
Key string
Fn func(v any) (any, bool, error)
}

RegExp is a JavaScript regular expression. Kit rejects these as remote function arguments, but the wire format can carry them.

type RegExp struct {
Source string
Flags string
}

A Replacer is devalue’s `uneval` replacer hook: a chance to emit custom JavaScript for a value before the built-in type handling sees it.

It is called once for each non-primitive value, on first encounter. When it returns ok, the trusted JavaScript string it returns is spliced into the output verbatim and the value’s contents are not walked. Replacer is not a flat-format Reducer. `uneval` is a nested emitter for producing the expression of a sub-value — SvelteKit’s transport uses it to write `app.decode(“name”, <uneval(encoded)>)`.

The nested emitter runs in its own namespace, exactly as devalue’s does, so references it emits are not shared with the enclosing document.

type Replacer func(v any, uneval func(any) (string, error)) (string, bool, error)

Set is a JavaScript Set: ordered items, deduplicated by identity.

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

func NewSet(items ...any) *Set

NewSet builds a Set from its items.

func (s *Set) Add(v any)

Add appends an item unless an identical one is already present.

func (s *Set) Has(v any) bool

Has reports whether an identical item is present.

func (s *Set) Items() []any

Items returns the items in insertion order.

func (s *Set) Len() int

Len returns the number of items.

Temporal is a Temporal value, held as its kind and its `toString()` form — the round-trip form `Kind.from(…)` accepts.

type Temporal struct {
Kind TemporalKind
Value string
}

TemporalKind names one of the Temporal types devalue serializes. It is the JavaScript constructor path, which is also what the emitted expression uses.

type TemporalKind string

The Temporal types devalue serializes.

const (
TemporalDuration TemporalKind = "Temporal.Duration"
TemporalInstant TemporalKind = "Temporal.Instant"
TemporalPlainDate TemporalKind = "Temporal.PlainDate"
TemporalPlainTime TemporalKind = "Temporal.PlainTime"
TemporalPlainDateTime TemporalKind = "Temporal.PlainDateTime"
TemporalPlainMonthDay TemporalKind = "Temporal.PlainMonthDay"
TemporalPlainYearMonth TemporalKind = "Temporal.PlainYearMonth"
TemporalZonedDateTime TemporalKind = "Temporal.ZonedDateTime"
)

TypedArray is a JavaScript typed array: a view of a byte range of an ArrayBuffer.

Buffer is always the *whole* underlying buffer, because that is what devalue serializes — the view’s own extent is emitted as a trailing `.subarray(a,b)`. Two views over the same Buffer value share a buffer in the emitted output, which is the property `new Uint16Array(uint8.buffer)` has in JavaScript.

Use a *TypedArray: identity, and therefore reference sharing, is the pointer’s.

type TypedArray struct {
Kind TypedArrayKind
Buffer ArrayBuffer
ByteOffset int
ByteLength int
}

func BigInt64ArrayOf(v ...int64) *TypedArray

func BigUint64ArrayOf(v ...uint64) *TypedArray

func Float32ArrayOf(v ...float32) *TypedArray

func Float64ArrayOf(v ...float64) *TypedArray

func Int16ArrayOf(v ...int16) *TypedArray

func Int32ArrayOf(v ...int32) *TypedArray

func Int8ArrayOf(v ...int8) *TypedArray

func NewTypedArray(kind TypedArrayKind, buf ArrayBuffer) *TypedArray

NewTypedArray views the whole of buf as kind.

func Uint16ArrayOf(v ...uint16) *TypedArray

func Uint32ArrayOf(v ...uint32) *TypedArray

func Uint8ArrayOf(v ...uint8) *TypedArray

func Uint8ClampedArrayOf(v ...uint8) *TypedArray

func (t *TypedArray) Len() int

Len returns the number of elements in the view.

func (t *TypedArray) Subarray(start, end int) *TypedArray

Subarray returns the [start, end) element range of t, as `TypedArray#subarray` does. It shares t’s buffer.

Serializing the result discloses the whole buffer, not just the range: Uneval writes every byte of Buffer and selects the range with a trailing `.subarray(start,end)`. Serialize a subarray only if its entire buffer is safe to disclose, or copy the range into a buffer of its own first:

own := NewTypedArray(sub.Kind, append(ArrayBuffer(nil), sub.Buffer[sub.ByteOffset:sub.ByteOffset+sub.ByteLength]...))

TypedArrayKind names a JavaScript typed array constructor.

type TypedArrayKind string

The typed array kinds devalue serializes. Float16Array is deliberately absent: Go has no half-precision float and devalue’s own test suite never exercises it.

const (
Int8Array TypedArrayKind = "Int8Array"
Uint8Array TypedArrayKind = "Uint8Array"
Uint8ClampedArray TypedArrayKind = "Uint8ClampedArray"
Int16Array TypedArrayKind = "Int16Array"
Uint16Array TypedArrayKind = "Uint16Array"
Int32Array TypedArrayKind = "Int32Array"
Uint32Array TypedArrayKind = "Uint32Array"
Float32Array TypedArrayKind = "Float32Array"
Float64Array TypedArrayKind = "Float64Array"
BigInt64Array TypedArrayKind = "BigInt64Array"
BigUint64Array TypedArrayKind = "BigUint64Array"
)

func (k TypedArrayKind) BytesPerElement() int

BytesPerElement is the kind’s BYTES_PER_ELEMENT, or 0 if the kind is unknown.

URL is a JavaScript URL, held as the serialized href returned by URL.prototype.toString. The caller owns WHATWG URL normalization.

type URL string

URLSearchParams is a JavaScript URLSearchParams, held as its serialized query string — what `params.toString()` returns.

type URLSearchParams string

UndefinedValue is the type of Undefined.

type UndefinedValue struct{}

func (UndefinedValue) MarshalJSON() ([]byte, error)

MarshalJSON renders `undefined` as null, which is what JSON.stringify does with it inside an array.

func (UndefinedValue) String() string
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/polytype/devalue.

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