🦖

JSONSchemaから直接Typescriptの型を推測するの書いた

2022/02/17に公開

なんか書けそうだったので書きました。

https://github.com/trkbt10/json-schema-type-inferrer

JSONSchemaを書きつつTypescriptも書きつつで二重定義が辛かったのを解消してくれると思います。

使い方

import type { InferJSONSchema } from "@trkbt10/json-schema-type-inferrer";

const additionalProperties = {
  type: "object",
  properties: {
    item: {
      type: "string",
    },
  },
  additionalProperties: {
    type: "string",
  },
  required: ["item"],
} as const;

const object: InferJSONSchema<typeof additionalProperties> = {
  item: "value",
}; //  { item: string; } & {} & { [key: string]: string;  }

const integerSchema = {
  type: "integer",
} as const;
const integer: InferJSONSchema<typeof integerSchema> = 1; // number

このように既存の型をGenericsで与えるだけで、変数に型をつけることができます。

作った理由

extendsとinferの素振りがてら、Ajvとの相互運用の観点から一度の定義だけで全部賄えるものが欲しかったためです。
正直、実装は割と簡単だったので、「すでに誰かやってるだろう」と思って調べてたんですが、変換するのはあれども直接推測するものは探し出すことができませんでした。
すでにあるものを教えてくれる人を探すのもこれを作った目的の一つです。知っている人は教えてください。

課題など

Reference型

$refの先にある定義をコピペするためのものですが、この定義から型をもう一度参照し直すのは思いついていないです。
Templateとinferを利用して取得することができそうです。

type SlicePath<S extends string> = S extends `${infer U}/${infer U2}`
  ? [U, ...SlicePath<U2>]
  : S extends `${infer U}`
  ? [U]
  : [];
type GetByKey<
  S extends { [key: string]: any },
  K extends any
> = K extends keyof S ? S[K] : never;

type Get<O extends { [key: string]: any }, P extends string[]> = P extends [
  infer U
]
  ? GetByKey<O, U>
  : P extends [infer U, ...infer U2]
  ? U2 extends string[]
    ? Get<GetByKey<O, U>, U2>
    : never
  : never;

このような型を定義すると、

type GetValue = Get<
  { parent: { child: { item: "item" } } },
  SlicePath<"parent/child/item">
>;

とすることで item を取得できます。

minLength, maxLength

再帰を利用し、Arrayのlengthを利用することで数の参照をできることは分かりましたが、比較することができないため、力技にならざるを得ません。
型つけるだけが目的なので諦めました。

Discussion