Open9

JSON Typed Definition のメモ

kiaikiai

TypeScript をやっていて JSON レスポンスに型を付けてェ~~~ついでに検証もしてほしいナ……などと思っていたら,JSON Type Definition なるものが出てきそうな機運らしい
現時点では RFC 8927 という Experimental なものだった

※RFC: Request for comments って文字列は見たことあってもきちんと調べたことはなかったので,詳しく知りたい人はまずこちらの概説をどうぞ

kiaikiai

順番が前後するけど,はじめに JTD という概念を見つけたのは以下の記事の紹介だった

これによると,歴史的には「JSON Schema」「Open API」「CDDL(Concise Data Definition Language)」がデータの構造定義(いわゆるスキーマ)を行なう上で採用されているが,どれも完璧ではなく,以下のような要求が残るらしい

さて、ここまで見てきて、ポリモーフィックパターンを扱う場合の、スキーマ記述言語に求める条件が明確になってきました。

  1. JSON Schema/OpenAPIのようにスキーマ定義を独自の構文では無く、JSONで記述できること
  2. OpenAPIのdiscriminatorキーワードのようなサブスキーマの特定機能があること
  3. CDDLのように仕様がコンパクトであること
kiaikiai

ここで登場するのが JTD こと JSON Typed Definition だ
筆者は更に続けている:

JTDのコンセプトはズバリ、

Its main goals are to enable code generation from schemas as well as portable validation with standardized error indicators.

と、バリデーション処理の自動生成のためのスキーマ記述仕様が目標に掲げられています。

>> バリデーション処理の自動生成 <<

最高の響きじゃあないか,俺の求めていたものに違いないッ!!👈👈👈👈

kiaikiai

2021年11月初旬時点では,typescript json validator と検索して出てくるのは,TypeScript JSON Validator が先頭(そのまんま過ぎてウケる)か「Ajv JSON schema validator」ぐらいだった

どっちを使うかと考えたときに,やはり将来的な長期メンテナンスを鑑みるに,スター数やドキュメントのみやすさ,コミュニティすべてにおいてAjvを採用する他なかった……(スポンサーには moz://a と Microsoft がついているので信頼爆アゲ)


注)このときに実は jtd も見つけているのだが,二つよりも圧倒的に少ないスター数でよろしくないものと勘違いし,早々に見切りをつけている……が,実はこれこそが本家本元の著者実装で,一番信頼性の高いものであることが後々明らかになった🤦

kiaikiai

JTD で定義したスキーマを Ajv で使うためには,まずそのスキーマ定義が必要となる

Using with TypeScript | Ajv JSON schema validator を読みつつやっていこうとするも,さっそく properties やら optionalProperties が含まれる schema に出鼻をくじかれた……

サンプルを理解する前に,まずは Ajv の解説を読む……う~んよくわからん

自分が勉強するついでに,他の日本語ネイティヴにも知見を広めるべく,全部和訳することを決意した

cf. JSON Type Definition | Ajv JSON schema validator

kiaikiai

記事単体で書き出そうとしたが,あまりにも短くなってしまったのでここに書き残す:

JSON Typed Definition の未来の話

====> schema.jtd.jsonとして切り出せるはず

スキーマ定義を JSON ファイルとして切り出してしまえば,TypeScript だろうと Rails や Django だろうと,言語を選ばずクロスプラットフォームな対応ができるようになる.そして実際に各言語での実装がすでに存在している

https://jsontypedef.com/docs/implementations/

Ajvを紹介した記事においては,コード内にスキーマを定義した上で as const による 型アサーションを行なっていた.これをコードから切り離し,JSON ファイルとして独立させることができれば,大望は実現するはず……

kiaikiai

が,そうは問屋が卸さない:

TypeScript 2.9 ~ においては, tsconfig.jsonresolveJsonModulemoduleResolution を指定すれば,JSON を直接インポートできるようになった.しかし,それをそのまま as const してやることは今のところ出来ないらしい
https://github.com/microsoft/TypeScript/issues/32063

import user from 'user.jtd.json'
const schema = { ...user } as const

という方法も試したが,これもどうにもうまく行かない(再帰的な型アサーションが難しいという事情があるのかも?)


これだけ期待されているところを見るに,来年後半までには実装されそうな予感はするが,ともかく現時点では現実的ではないようだ……

どうにか回避できる方法はないだろうかか?
Zenn Contributor たちの知見を求めたい

kiaikiai

JTD を採用したバリデータの JS 実装は,現時点で ajvjtd の二つがある:前者はJSバリデータのデファクト・スタンダードたる老舗ライブラリで,後者が RFC 8927 (Experimental) の著者による本家本元の実装である.

ajv の長所としては,スキーマから型定義を生成できることやスキーマを受け取ってコンパイルすることが挙げられる.これにより速度が担保されるし,パーサやシリアライザにも転用できる.ただし,そのスキーマが正しいものであるかについては判断が難しい.
一方で jtd は,高速化や型定義生成,パーサやシリアライザを得ることは出来ないが,確実に安全性が担保されたスキーマ判定ができる.

つまり,JTDのスキーマ定義を完了させるまでは jtd を使い,いざプロダクションとなったときに ajv を使うのがいいと考えた.となると,jtd によるスキーマ定義部分も手元でやるのは面倒に感じる……と考えて作ったのが JTDV である.

https://jtd-validator.vercel.app/

任意の文字列を受け取って,それが JTD のスキーマ定義に沿っているか検証するツールである.スキーマ設計の段階ではこのツールを使い,実際に定義できたら,Ajvでバリデータ・パーサ・シリアライザを作って使うのが賢いだろう.