TypeScriptでJSONの型チェックめんどくさい
そんなときはコレ
ts-json-checker
セールスポイント
- インターフェイス型などから型チェック関数を自動生成
- (チェッカーと言いつつ)指定した型を変換する機能あり
- ただの関数なので何が実行されるのか簡単に読める
- 実行時のライブラリ依存なし
「TypeScript JSON」でググると実験的機能なリフレクションで実現していたりして、いかにも重たそうで、もっとシンプルなコードにしたいと思っていました。
そこでts-json-checkerでは、TypeScript Compiler APIを使用し型を解析して、型チェック関数を生成するようにしました。
使い方
npmからインストールして
npm install --save-dev ts-json-checker
設定ファイルを書いて
// ts-json-config.ts
import { generate } from 'ts-json-checker'
const fileName = './generated.ts'
export interface Product {
name: string
price: number
release: string
}
generate<Product>('parseProduct')
実行する
npx ts-json-checker
おわり!
これで設定に書かれたファイル名('./generated.ts')に型チェック関数モジュールが出力されています。
// generated.ts
export interface Product {
name: string;
price: number;
release: string;
}
export function parseProduct(v: any): Product {
if (v !== null && typeof v === "object")
__check_1(v, "v");
else
throw new TypeError("v is not Object.");
return <Product>v;
}
function __check_1(v: any, r: string) {
if (typeof v.name === "string") { }
else
throw new TypeError(r + ".name is not String.");
if (typeof v.price === "number") { }
else
throw new TypeError(r + ".price is not Number.");
if (typeof v.release === "string") { }
else
throw new TypeError(r + ".release is not String.");
}
これはただの関数なのでインポートするだけで使えます。
// test.ts
import { parseProduct } from './generated'
const productJson = `{
"name": "json type check book",
"price": "1980",
"release": "2019-03-02"
}`
try {
const product = parseProduct(JSON.parse(productJson))
console.log(product)
} catch (err) {
if (err instanceof TypeError) {
console.error('invalid product type:', err.message)
} else {
throw err
}
}
npx ts-node test.ts
で実行するとinvalid product type: v.price is not Number.
と例外が発生します。
特定のプロパティを変換
上記JSONのprice
は数値ですがstring
で表現されています。これをnumber
に変換しましょう。
convertProp
でプロパティの変換を指定します。
// ts-json-config.ts
// convertPropをインポート
import { generate, convertProp } from 'ts-json-checker'
// Product型のpriceをstringからnumberに変換
convertProp<Product['price']>(v => {
if (typeof v !== "string") throw new Error('price is not string.')
const value = parseInt(v, 10)
if (isNaN(value)) throw new Error('Unable to convert to number. value: ' + v)
return value
})
npx ts-json-checker
npx ts-node test.ts
を実行すると
{ name: 'json type check book',
price: 1980,
release: '2019-03-02' }
と表示され、price
がnumber
に変換されていることが確認できます。
型変換
また、型ごとにカスタム変換を行う機能があります。Product型のrelease
をstring
からDate
に変換しましょう。
convert
関数で型変換を指定します。
// ts-json-config.ts
// convertをインポート
import { generate, convert, convertProp } from 'ts-json-checker'
const fileName = './generated.ts'
export interface Product {
name: string
price: number
release: Date // Date型に変更
}
// 文字列をDate型に変換
convert<Date>(v => {
const dt = typeof v === "string" ? Date.parse(v) : NaN
if (isNaN(dt)) throw new TypeError('Unable to convert to Date. value: ' + v)
return new Date(dt)
})
もう一度
npx ts-json-checker
npx ts-node test.ts
を実行します。
{ name: 'json type check book',
price: 1980,
release: 2019-03-02T00:00:00.000Z }
と表示され、string
がDate
型に変換されていることが分かります。
設定ファイルの例
設定ファイルで記述できる型の例はサンプルを見てください。大体網羅できてると思います。
おわりに
上記の対応しました。price
のstring
/number
変換のような機能は欲しいと思いますので、今後実装していきたいです。
それと、設定ファイル内での型定義(インターフェイス宣言や型エイリアス)が無視される状態なので、これも使えるようにしたいです。
他に実現したいシナリオなどコメントやイシューを頂ければ幸いです。