LoginSignup
8
5

More than 5 years have passed since last update.

TypeScriptでJSONの型チェックをするための関数ジェネレーター ts-json-checker

Last updated at Posted at 2019-03-02

TypeScriptでJSONの型チェックめんどくさい:angry:
そんなときはコレ:innocent::point_up:

ts-json-checker

GitHubリポジトリ

セールスポイント

  • インターフェイス型などから型チェック関数を自動生成
  • (チェッカーと言いつつ)指定した型を変換する機能あり
  • ただの関数なので何が実行されるのか簡単に読める
  • 実行時のライブラリ依存なし

「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

おわり!:ok_hand:
これで設定に書かれたファイル名('./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.と例外が発生します。:v:

特定のプロパティを変換

上記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' }

と表示され、pricenumberに変換されていることが確認できます。

型変換

また、型ごとにカスタム変換を行う機能があります。Product型のreleasestringから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 }

と表示され、stringDate型に変換されていることが分かります。:muscle:

設定ファイルの例

設定ファイルで記述できる型の例はサンプルを見てください。大体網羅できてると思います。

おわりに

上記のpricestring/number変換のような機能は欲しいと思いますので、今後実装していきたいです。
それと、設定ファイル内での型定義(インターフェイス宣言や型エイリアス)が無視される状態なので、これも使えるようにしたいです。
対応しました。

他に実現したいシナリオなどコメントやイシューを頂ければ幸いです。

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5