TypeScriptベースのJSON Schema実装と開発ツール集
(github.com/imhonglu)「自分好みの型安全なライブラリを作ってみよう」という思いで始めたプロジェクトです。
このプロジェクトは、型安全なJSON Schema実装を出発点として、開発プロセスで必要になるさまざまなツールへと自然に拡張されていきました。
現在、求職のためにひとまず一区切りを付けてみました。
プロジェクト原則
次のような中核原則を守って開発しました:
- 厳格な型システムの活用
- 外部依存を最小限に維持
- 再利用可能な型システムの設計
- APIのドキュメント化
- 高いテストカバレッジの維持
- 純粋なTypeScript実装
ライブラリ
@imhonglu/json-schema
JSON Schema 2020-12 draft仕様に準拠したTypeScript実装です。
- リポジトリ: https://github.com/imhonglu/new-wheels/…
JSON-Schema-Test-Suiteによる検証- スキーマ定義に応じて利用可能なキーワードの型が自動推論されます。
import { Schema, SchemaDefinition } from "@imhonglu/json-schema";
export const Address = new Schema({
type: "object",
properties: {
street: { type: "string" },
city: { type: "string" },
zip: { type: "string" },
},
required: ["street"] as const,
});
export type Address = SchemaDefinition.Instance<typeof Address>;
// {
// street: string;
// city?: string;
// zip?: string;
// }
@imhonglu/format
JSON Schemaのformatキーワードを実装するために始まったプロジェクトです。
- リポジトリ: https://github.com/imhonglu/new-wheels/…
- RFC仕様ベースの実装
JSON-Schema-Test-Suiteベースの検証- ネイティブ
JSONAPIに似たインターフェースを提供
import { FullTime } from '@imhonglu/format';
const time = FullTime.parse('00:00:00.000Z');
// { hour: 0, minute: 0, second: 0, secfrac: '.000', offset: undefined }
console.log(time.toString()); // '00:00:00.000Z'
console.log(JSON.stringify(time)); // '"00:00:00.000Z"'
const result = FullTime.safeParse('invalid');
if (!result.ok) {
console.error(result.error);
}
@imhonglu/pattern-builder
RFC仕様のABNF文法を実装する中で、正規表現の可読性向上のために作られたregexビルダーです。
import { characterSet, concat, hexDigit } from "@imhonglu/pattern-builder";
// pct-encoded = "%" HEXDIG HEXDIG
export const pctEncoded = concat(
"%",
hexDigit.clone().exact(2),
);
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
export const unreserved = characterSet(
alpha,
digit,
/[\-._~]/,
);
@imhonglu/type-guard
型ガードの可読性向上のために作られた型ガードライブラリです。
- リポジトリ: https://github.com/imhonglu/new-wheels/…
- Proxyベースの実装でオーバーヘッドを最小化
notキーワードを提供
import { composeGuards } from "@imhonglu/type-guard";
const is = composeGuards({
string: (value: unknown): value is string => typeof value === "string",
number: (value: unknown): value is number => typeof value === "number"
});
is.string("hello"); // true
is.not.string(42); // true
let value: string | number | undefined;
if (is.number(value)) {
value.toFixed(2); // 'value' is number
}
if (is.not.number(value)) {
value.toFixed(2); // error: Property 'toFixed' does not exist on type 'undefined'.
}
@imhonglu/type-object
ネイティブObject APIの型安全なラッパーライブラリです。ネイティブの動作に近い型を提供します。
import * as TypeObject from '@imhonglu/type-object';
const data = { a: 1, b: 2, c: 3 };
for (const key of TypeObject.keys(data)) {
// key: "a" | "b" | "c"
console.log(data[key]); // number
}
const string = 'hello';
for (const index of TypeObject.keys(string)) {
// index: number & keyof string
console.log(string[index]); // string
}
@imhonglu/toolkit
プロジェクト内部で使っているユーティリティ型とユーティリティ関数のコレクションです。
import type { Fn } from '@imhonglu/toolkit';
// 関数型 '(...args: any[]) => any' に対する型エイリアスを提供します。
Fn.Callable // (...args: any[]) => any
// ジェネリクスを通じて引数の型だけを定義できます。
Fn.Callable<{ args: [number, number] }> // (...args: [number, number]) => any
// ジェネリクスを通じて戻り値の型だけを定義できます。
Fn.Callable<{ return: string }> // (...args: any[]) => string
// ジェネリクスを通じて引数の型と戻り値の型を両方定義できます。
Fn.Callable<{ args: [number, number], return: string }> // (...args: [number, number]) => string
今後の計画と求職
進行中のプロジェクトの次の段階では、JSON Schema仕様の実装を仕上げ、
バックエンドフレームワークも書いてみたいと思っています。
現在求職中ですので、ぜひご関心をお寄せください。
読んでいただきありがとうございました。
良い一日をお過ごしください!
2件のコメント
こちらの分野では
zodという非常に優れたものがあるので、プロダクトではそれを使っていますが、興味深いですね。ajv、typia、zod などの既存プロジェクトは、私も関心を持って見ているプロジェクトです。
@imhonglu/formatのsafeParseも、zod API から影響を受けた機能です。ご関心をお寄せいただき、ありがとうございます!