AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Compile JSON Schema to TypeScript typings.
Check out the live demo.
Requires Node.js 22.19 or later. (json-schema-to-typescript 16.x is the last release line that runs on Node.js 16–20.)
json-schema-to-typescript is easy to use via the CLI, or programmatically.
First make the CLI available using one of the following options:
Then, use the CLI to convert JSON files to TypeScript typings:README 전문 보기 ↗Compile JSON Schema to TypeScript typings.
Check out the live demo.
Input:
{
"title": "Example Schema",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
},
"hairColor": {
"enum": ["black", "brown", "blue"],
"type": "string"
}
},
"additionalProperties": false,
"required": ["firstName", "lastName"]
}
Output:
export interface ExampleSchema {
firstName: string;
lastName: string;
/**
* Age in years
*/
age?: number;
hairColor?: "black" | "brown" | "blue";
}
npm install json-schema-to-typescript
Requires Node.js 22.19 or later. (json-schema-to-typescript 16.x is the last release line that runs on Node.js 16–20.)
json-schema-to-typescript is easy to use via the CLI, or programmatically.
First make the CLI available using one of the following options:
# install locally, then use `npx json2ts`
npm install json-schema-to-typescript
# or install globally, then use `json2ts`
npm install json-schema-to-typescript --global
# or install to npm cache, then use `npx --package=json-schema-to-typescript json2ts`
# (you don't need to run an install command first)
Then, use the CLI to convert JSON files to TypeScript typings:
cat foo.json | json2ts > foo.d.ts
# or
json2ts foo.json > foo.d.ts
# or
json2ts foo.yaml foo.d.ts
# or
json2ts --input foo.json --output foo.d.ts
# or
json2ts -i foo.json -o foo.d.ts
# or (quote globs so that your shell doesn't expand them)
json2ts -i 'schemas/**/*.json' -o types/
# or
json2ts -i schemas/ -o types/
You can pass any of the options described below (including style options) as CLI flags. Boolean values can be set to false using the no- prefix.
The CLI automatically loads the closest Prettier configuration for the generated output file (when writing to stdout: for a .d.ts next to the input file, or in the working directory for piped input). Explicit --style.* flags take precedence over discovered settings, and the output is always parsed as TypeScript whatever parser the config names. A Prettier config that cannot be loaded (invalid syntax, a missing plugin) now fails the run. This does not affect the programmatic API.
# generate code for definitions that aren't referenced
json2ts -i foo.json -o foo.d.ts --unreachableDefinitions
# use single quotes and disable trailing semicolons
json2ts -i foo.json -o foo.d.ts --style.singleQuote --no-style.semi
# pass options to the $ref resolver (quote the flag, so that your shell leaves the `$` alone)
json2ts -i foo.json -o foo.d.ts '--$refOptions.dereference.externalReferenceResolution=root'
By default each input file is compiled on its own, so a type that several files reach through a $ref is declared again in every output file. With --imports, a directory or glob is compiled together: a type that lives in another file of the set becomes an import type from that file's module instead of a copy.
json2ts -i schemas/ -o types/ --imports
For example, with common.json holding shared definitions, and a.json and b.json using them and each other:
// schemas/common.json
{
"title": "Common",
"type": "object",
"additionalProperties": false,
"definitions": {
"thing": {
"title": "Thing",
"type": "object",
"properties": {"id": {"type": "string"}, "tags": {"type": "array", "items": {"$ref": "#/definitions/tag"}}},
"required": ["id"],
"additionalProperties": false
},
"tag": {"title": "Tag", "type": "string", "enum": ["red", "green"]}
}
}
// schemas/a.json
{
"title": "A",
"type": "object",
"properties": {"thing": {"$ref": "common.json#/definitions/thing"}, "partner": {"$ref": "b.json"}},
"additionalProperties": false
}
// schemas/b.json
{
"title": "B",
"type": "object",
"properties": {"thing": {"$ref": "common.json#/definitions/thing"}, "owner": {"$ref": "a.json"}},
"additionalProperties": false
}
// types/a.d.ts
import type {B} from "./b.js";
import type {Thing} from "./common.js";
export interface A {
thing?: Thing;
partner?: B;
}
// types/b.d.ts
import type {A} from "./a.js";
import type {Thing} from "./common.js";
export interface B {
thing?: Thing;
owner?: A;
}
// types/common.d.ts
export type Tag = "red" | "green";
export interface Common {}
export interface Thing {
id: string;
tags?: Tag[];
}
(Without --imports, a.d.ts and b.d.ts each declare their own Thing, Tag, A and B, plus a second A1/B1 where the cycle comes back around, and common.d.ts declares only Common.)
What each file gets:
definitions/$defs, and any other named schema its root type reaches. A $ref to anything else in it (say other.json#/properties/x with no title), to a file outside the set, or to a URL, is declared inline as before; so is a $ref that carries keywords of its own (eg. a description), since it describes a different type. Schemas under other keys, such as OpenAPI's components/schemas, are not importable yet.definitions are declared whether or not the file uses them (unreachableDefinitions is on for the set), so a file's output is what json2ts thatFile.json --unreachableDefinitions prints, minus the declarations that now come from an import, plus the import type lines.import type {Thing as Thing1}). Files that $ref each other in a cycle are fine..js, which TypeScript resolves to the .d.ts (or .ts) next to it under every moduleResolution setting.$refs resolve against the file they appear in; --cwd cannot be combined with --imports, and --imports needs an output directory.The same is available programmatically as compileFiles, which returns the TypeScript for each file, in order, and writes nothing (the output paths are only used to compute the import paths):
import { compileFiles } from 'json-schema-to-typescript'
const [a, b, common] = await compileFiles(
[
{filename: 'schemas/a.json', outputPath: 'types/a.d.ts'},
{filename: 'schemas/b.json', outputPath: 'types/b.d.ts'},
{filename: 'schemas/common.json', outputPath: 'types/common.d.ts'},
],
{bannerComment: ''},
)
To invoke json-schema-to-typescript from your TypeScript or JavaScript program, import it and call compile or compileFromFile.
import { compile, compileFromFile } from 'json-schema-to-typescript'
// compile from file
compileFromFile('foo.json')
.then(ts => fs.writeFileSync('foo.d.ts', ts))
// or, compile a JS object
let mySchema = {
properties: [...]
}
compile(mySchema, 'MySchema')
.then(ts => ...)
See server demo and browser demo for full examples.
compileFromFile and compile accept options as their last argument (all keys are optional):
| key | type | default | description |
|---|---|---|---|
| additionalProperties | boolean | true | Default value for additionalProperties, when it is not explicitly set |
| bannerComment | string | "/* eslint-disable */\n/**\n * This file was automatically generated by json-schema-to-typescript.\n * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,\n * and run json-schema-to-typescript to regenerate this file.\n */" | Disclaimer comment prepended to the top of each generated file |
| customName | (LinkedJSONSchema, string | undefined) => string | undefined | undefined | Custom function to provide a type name for a given schema |
| cwd | string | process.cwd() | Root directory for resolving $refs (for compileFiles: the directory its relative filenames and outputPaths are taken from; $refs then resolve against each file) |
| declarationStyle | 'interface' | 'type' | 'interface' | Declare object types as interfaces (export interface B extends A {…}) or as type aliases (export type B = A & {…}) |
| declareExternallyReferenced | boolean | true | Declare external schemas referenced via $ref? |
| enableConstEnums | boolean | true | Prepend enums with const? |
| inferStringEnumKeysFromValues | boolean | false | Create enums from JSON enums with eponymous keys |
| format | boolean | true | Format the output with Prettier? With the output is still indented and readable, just not line-wrapped by Prettier, and large schemas compile two to three times faster in about half the memory (see ). |
This repo uses bun (1.3.9 or later) to install dependencies and run its scripts and tests, so install it first:
$ bun install
$ bun run test
title => interfacedeprecatedallOf ("intersection")anyOf ("union")tsType: Overrides the type that's generated from the schema. Useful for forcing a type to any or when using non-standard JSON schema extensions (eg).tsEnumNames: Overrides the names used for the elements in an enum. Can also be used to create string enums (eg). The names must be distinct strings, one per enum value; otherwise the schema is rejected with a ValidationError. A name TypeScript would read as a number ("1", "-1", "2.5") cannot be an enum member's name, so it gets a leading underscore (_1 = 1); the same goes for a value that inferStringEnumKeysFromValues turns into a name. A TypeScript enum holds strings and numbers only: an enum with a null, boolean, object or array value is typed as a union of its values (null | "a" | "b") instead, and the names go unused.dependencies (single, multiple)divisibleBy (eg)format (eg) — but see the formatTypes option to map a format to a type of your choosingmultipleOf (eg)maximum (eg)minimum (eg)maxProperties (eg)minProperties (eg)not/disallowoneOf ("xor", use anyOf instead)pattern (string, regex)Every draft goes through the same pipeline; a $schema declaration does not change the output. The schema model is draft 4 (JSONSchema4 from @types/json-schema): the lists above cover its keywords, the table below covers what later drafts added. "Supported" means the keyword shapes the emitted type as the spec intends; keywords that only constrain values have no TypeScript equivalent and are ignored next to a type (a subschema made of nothing else still comes out as {[k: string]: unknown} rather than unknown, #806 pending). As of master d86f285, which is ahead of 16.0.0 (there unevaluatedProperties is still ignored and formatTypes does not exist); every row was checked by compiling a one-keyword schema with the CLI.
| Keyword | Status | Note | Tracking |
|---|---|---|---|
| draft 6 | |||
const | supported | literal types, including object literals | |
boolean schemas (true / false) | supported | as a property, items, additionalProperties, and inside allOf / anyOf. A $ref to one crashes (#809 pending); a root true / false errors | #725, #496 |
$id | supported | for naming, and $ref: "#name" to an $id: "#name" | |
examples | ignored | not copied into the JSDoc comment | #237 |
propertyNames | ignored | enum / const names would be expressible | #337 |
contains, numeric exclusiveMinimum / exclusiveMaximum | not expressible | ||
| draft 7 | |||
if / then / else | ignored | properties and required inside the branches contribute nothing | #426 |
readOnly | ignored | a readonly modifier is #796 (pending) |
Not supported from 2019-09 / 2020-12, by name: $anchor, $recursiveRef, $recursiveAnchor, $dynamicRef, $dynamicAnchor, $vocabulary, unevaluatedItems, prefixItems, dependentSchemas, dependentRequired, minContains, maxContains, contentSchema, and $id-based reference resolution.
Turn formatting off: --no-format on the command line, format: false in the API. Most of the time and much of the memory of compiling a large schema go to Prettier, and the unformatted output is indented, one member per line, and perfectly readable — it just isn't wrapped to a line width. Measured on this repository's benchmark set (node bench/bench.mjs, Node 22):
| schema | output | format: true | format: false |
|---|---|---|---|
| Azure deployment template (60 linked schemas) | 8.6 MB | 8.3 s, 958 MB | 3.7 s, 415 MB |
| FHIR R4 | 2.3 MB | 0.89 s, 266 MB | 0.32 s, 164 MB |
Kubernetes definitions (unreachableDefinitions) | 0.9 MB | 0.35 s, 155 MB | 0.11 s, 119 MB |
(wall-clock time of one compile() call and the peak memory of the process.) For anything you generate in a build step rather than commit and read, format: false is the recommended setting.
false| formatTypes | Record<string, string> | {} | Map from a string schema's format to the TypeScript type to emit for it, verbatim, like tsType (which still takes precedence, as do enum and const). Eg. { 'date-time': 'Date' } turns { "type": "string", "format": "date-time" } into Date instead of string; nullable, arrays and $refs follow (Date | null, Date[]). Formats you don't list stay string. For a type of your own, add its import via bannerComment. CLI: --formatTypes.date-time=Date. |
| ignoreMinAndMaxItems | boolean | false | Ignore maxItems and minItems for array types, preventing tuples being generated. |
| maxItems | number | 20 | Maximum number of unioned tuples to emit when representing bounded-size array types, before falling back to emitting unbounded arrays. Increase this to improve precision of emitted types, decrease it to improve performance, or set it to -1 to ignore maxItems. |
| readonly | boolean | false | Mark every property and index signature readonly, and emit every array and tuple type as readonly T[]. |
| readonlyKeyword | boolean | false | Map the schema's readOnly: true annotation to TypeScript's readonly: an annotated property gets the readonly modifier, and an annotated array or tuple is emitted as readonly T[]. |
| removeOptionalIfDefaultExists | boolean | false | Remove the optional modifier when a property has a default value. |
| strictIndexSignatures | boolean | false | Append all index signatures with | undefined so that they are strictly typed. |
| style | object | { bracketSpacing: false, printWidth: 120, semi: true, singleQuote: false, tabWidth: 2, trailingComma: 'none', useTabs: false } | A Prettier configuration |
| undefinedOptionalProperties | boolean | false | Append | undefined to the type of every optional property (age?: number | undefined), for consumers that compile with TypeScript's exactOptionalPropertyTypes. |
| unknownAny | boolean | true | Use unknown instead of any where possible |
| unreachableDefinitions | boolean | false | Generates code for $defs that aren't referenced by the schema. |
| $refOptions | object | {} | $RefParser Options, used when resolving $refs. HTTP(S) $refs to loopback, private-network and internal hosts (localhost, 127.*, 10.*, 192.168.*, *.internal, *.corp, *.local…) or to ports such as 8080 and 8443 are refused by default (Unable to resolve $ref pointer "http://localhost…"); pass {resolve: {http: {safeUrlResolver: false}}} to allow them |
oneOfanyOfmaxItems (eg)minItems (eg)items + additionalItems (draft 4 – 2019-09) and prefixItems + items (draft 2020-12)additionalProperties of typepatternProperties (partial support)required properties on objects (eg)validateRequired (draft 3 style required: true on a property) (eg)tsTypereadOnly → readonly properties and arrays (readonlyKeyword option)uniqueItems (eg)| #131 |
writeOnly, $comment, contentMediaType, contentEncoding, new formats | not expressible | formats are plain string unless mapped with the formatTypes option |
| 2019-09 |
$defs | supported | same as definitions (using both in one schema errors) |
$anchor | errors | a $ref: "#name" to it fails with "Refs should have been resolved by the resolver"; $id: "#name" or a JSON pointer works |
$id inside a subschema as a base URI | errors | $refs resolve against the file's location, not against an enclosing $id |
$ref with sibling keywords | supported | siblings that extend the referenced schema (properties, patternProperties, required, items, allOf / anyOf / oneOf) are intersected with it, as the equivalent allOf (Foo & {...}); annotations (description, title, default...) stay on the property and the reference resolves to Foo itself. Siblings that restate or adjust the referenced schema (type alone, enum, const, additionalProperties, minItems / maxItems, format) and tsType are still merged into a copy of it, which may be emitted as a second type (Foo1) | #613 |
$recursiveRef / $recursiveAnchor | ignored | the target is typed {[k: string]: unknown} (unknown with #806, pending), not the recursive type |
unevaluatedProperties | partial | like additionalProperties for the schema's own properties; not enforced across allOf / anyOf / oneOf; wrongly closed over properties from a sibling $ref, dependentSchemas or if / then (#798 pending) |
unevaluatedItems, dependentSchemas | ignored |
dependentRequired, minContains / maxContains, contentSchema | not expressible | like draft 4 dependencies | #169 |
deprecated | supported | @deprecated in the JSDoc comment |
| 2020-12 |
prefixItems | ignored | unknown[]; with items: false → never[], with items: {…} that schema is applied to every element. Tuples are #816 (pending) | #543 |
$dynamicRef / $dynamicAnchor | ignored | as $recursiveRef |