Declarations
A project has exactly one marked configuration root. How to declare it as an annotated interface or a schema-only class, how property paths map to environment names, and which TypeScript constructs are rejected.
A project has exactly one marked, exported root in its configured input file. Nested interfaces and type declarations may be imported through the TypeScript program.
Interfaces
/** @typespun */
export interface AppConfig {
server: { host: string; port: number };
/** @default "development" */
mode: 'development' | 'production';
database?: { host: string; port: number };
}Interfaces use JSDoc annotations. @default accepts JSON text.
Schema-only classes
import { Config, Env, Secret } from 'typespun';
@Config()
export class AppConfig {
port = 3000;
@Env('DATABASE_URL')
@Secret()
databaseUrl!: string;
}Decorators are inert markers. Typespun does not instantiate the class or run constructors, getters, setters, or methods; those class members are rejected. Initializers must be statically readable strings, finite numbers, booleans, arrays, object literals, or string enum members. Function calls and imported runtime values are not executed.
Field mapping
Property paths become uppercase snake-case environment names. With
envPrefix: "APP", server.httpPort becomes APP_SERVER_HTTP_PORT. @env
sets a complete leaf environment name. @key changes the JSON/YAML defaults
segment only; it does not rename the TypeScript property or environment path.
Supported leaves are string, number, boolean, string literal unions or
string enums, and arrays of those three primitives. See the
annotation reference for exact forms.
Limitations
Not supported: JavaScript schemas, nullable unions, tuples, records and index signatures, dates, maps, sets, methods, computed fields, and recursive shapes. There is no plugin system, no asynchronous source and no runtime JSON/YAML loading, and a project has exactly one configuration root. Unsupported constructs fail generation rather than degrading silently.
Validation and redaction
loadConfig collects every issue and throws one ConfigError. Fields marked secret omit their received value and type-specific detail from Typespun's own diagnostics — redaction, not encryption.
Runtime API
The symbols exported from the typespun runtime package — ConfigError and ConfigIssue — with their exact shapes and the meaning of every issue field.