What you get from one config file
The sample loaded by the button above is a trimmed deployment file for toolexe.com. Three hosts, an owner block, a limits block, and a handful of top-level scalars. The output on the right is what the generator writes for the default settings, and the rest of this page explains each line of it.
service: toolexe-web
version: 3.14.0
released: 2026-09-04
replicas: 3
debug: false
owner:team: platform
email: platform@toolexe.com
on-call: Priya Raman
hosts:- name: edge-fra-01
region: eu-central
port: 443
tags: [cdn, tls]- name: edge-sin-02
region: ap-southeast
port: 8443
canary: true
- name: edge-iad-03
region: us-east
tags: []limits:cpu: 1.5
memory: 512Mi
timeout: ~export interface ToolexeConfig {service: string;version: string;released: string;replicas: number;debug: boolean;owner: Owner;hosts: Host[];limits: Limits;}
export interface Owner {team: string;email: string;"on-call": string;}
export interface Host {name: string;region: string;port?: number;tags?: string[];canary?: boolean;}
export interface Limits {cpu: number;memory: string;timeout: null;}Four lines in the output are worth a second look. version is a string, not a number, because 3.14.0 has two dots and no YAML parser reads it as a float. released is a string by default even though the parser produced a Date, for reasons covered below. port and tags are optional because the third host has no port and the second has no tags. timeout is typed as null alone, since the file gives no other value to widen the type with, and the notes list flags it so you replace it by hand.
How a value turns into a type
The generator parses the YAML with js-yaml under the 1.2 core schema, walks the resulting tree, and applies the rules below in order. Reading them once tells you what to expect from any file.
- Scalars map by parsed type. Quoted or unquoted text is
string. Integers, floats, hex, octal and.infarenumber.trueandfalseareboolean. YAML 1.1 spellings likeyesandoffare plain strings under the schema used here. - Nulls widen the type.
~,nulland a bare key with no value all parse as null. The default setting appends| nullto whatever other type the key carries. Switch to the optional setting and the key gets a?instead, which suits config files where an unset value and a missing key mean the same thing. - Mappings become named interfaces. Every nested mapping gets a declaration named after its key in PascalCase.
ownerbecomesOwner,rate-limitsbecomesRateLimits. Two keys producing the same name get a numeric suffix rather than a silent merge. - Lists become arrays of the merged item type. Item shapes are unioned. A list of mappings produces one interface named from the singular of the key, so
hostsgivesHostandpoliciesgivesPolicy. Keys missing from some items are marked optional. A list mixing strings and numbers is(string | number)[]. An empty list isunknown[]until another item elsewhere tells the generator more. - Keys are written as they are, with quotes when needed. A key with a dash, a dot, a space or a leading digit is not a valid identifier, so
on-callis emitted as"on-call". Pick the camelCase setting and the key is renamed toonCall, with a note reminding you the runtime object still holds the original spelling. - Multi-document files type the first document. A file split with
---is common for Kubernetes manifests, and each document usually has a different shape. The generator types the first one and says how many it skipped. Paste a single document to type a later one.
Why list items merge instead of picking the first
Most quick converters read the first item of a list and call it the type. On a file like the sample, the result is Host with a required port and no canary key at all, and the compiler then rejects the real object the moment you type it. Merging every item costs a little clarity in the output and pays for it with types the data satisfies.
The merge treats keys and types separately. A key present in every item is required. A key present in some items is optional. A key whose values disagree in type across items becomes a union, so a timeout written as 30 on one host and 30s on another is number | string. The notes list reports each of these, because a union of that kind usually means a mistake in the YAML rather than a real design choice.
Dates, timestamps and the string default
js-yaml turns an unquoted 2026-09-04 into a JavaScript Date. Whether your code sees a Date depends entirely on which parser reads the file at runtime. A Node service using js-yaml gets a Date. A Go or Python service serving the same config over JSON sends a string. A YAML file loaded with the { schema: JSON_SCHEMA } option also gets a string. The generator defaults to string because the string case is the one where a wrong type produces a runtime error rather than a compiler complaint. Flip the dates setting to Date when you know the file is parsed by js-yaml with the default schema and never crosses a JSON boundary.
Naming rules for generated declarations
| YAML key | Declaration | Why |
|---|---|---|
owner: | Owner | Mapping, PascalCase of the key |
hosts: | Host | List of mappings, singular of the key |
policies: | Policy | Trailing ies becomes y |
status: | Status | Words ending in ss or us are left alone |
rate-limits: | RateLimits | Dashes and underscores split words |
2fa: | Key2fa | Leading digit gets a prefix so the name compiles |
second owner: deeper in the tree | Owner2 | Collisions get a suffix, never a merge |
Singularizing is a heuristic and English is not regular. data stays Data, series becomes Serie, and a key like news gives New. Rename in the output when this bites, or rename the key in the YAML if you own the file.
Where the output falls short
- No literal or enum types. A key holding
productionis typedstring, not"production" | "staging". One file does not contain enough information to know the full set of allowed values, and guessing produces types a future value breaks. Write the union by hand where the set is closed. - Mappings keyed by ID are typed as fixed keys. A block like
servers: { web-01: {...}, web-02: {...} }produces an interface with literal"web-01"and"web-02"properties. If those names vary per environment, replace the declaration withRecord<string, Server>. The notes list points at mappings whose child values all share one shape, since those are the usual candidates. - Anchors and merge keys are resolved before typing. Values shared through
&defaultsand<<: *defaultsappear in every interface they land in. The output cannot express the shared origin, and a base interface withextendsis something you add afterwards. - Custom tags stop the parse. Files carrying
!Ref,!!python/objector other application tags fail with the line number of the first tag. Strip them, or type the file with the tool the tags belong to. - Big files run on the main thread. A few thousand lines convert in well under a second and re-run on every keystroke. Past a few megabytes the textarea starts to lag. At that size, run js-yaml in a script and paste a representative slice here instead.
- Nothing is uploaded. Parsing and generation happen in the browser after the page loads. A config holding internal hostnames and contact emails stays in the tab.
When a hand-written type is the better call
Generated interfaces are a starting point for config files and API fixtures you did not write. They are a poor fit for a schema you already maintain. If a JSON Schema exists for the file, the YAML to JSON Schema converter followed by a schema-to-types tool gives you validation and types from one source. If the YAML is a fixture for a JSON API, convert with the YAML to JSON converter first and type the JSON with the JSON to TypeScript converter, so the sample you type matches what the wire carries, dates included. And if the file fails to parse at all, the YAML validator lists every problem at once instead of stopping at the first.
