ts-serde - TypeScriptにおけるserdeライブラリ
導入
Rustにはシリアライズ・デシリアライズのインターフェースライブラリ(フレームワーク)としてserde
というものがあります。
私はTypeScriptをよく使うのでTypeScript版のserde
が欲しくなったので作りました。
とはいえ、Rustのような大規模なフレームワークではなく、シリアライズ・デシリアライズを統一した規格で行うためのライブラリセットです。
ts-serde
いつも通り依存関係をインストールします。
npm i ts-serde
型定義
ts-serde
からは3つの型定義がエクスポートされています。
import { Serde } from 'ts-serde'
import { Serialize, Deserialize } from 'ts-serde/types'
それぞれの中身は以下のようになっています。
type Serialize<T> = (val: T) => string
type Deserialize<T> = (str: string) => T
type Serde<T> = {
serialize: Serialize<T>
deserialize: Deserialize<T>
}
単純ですね。あとはこれを規格化して、serdeの複雑度を軽減していきます。
プリミティブ型
import { string, number, boolean, bigint, integer } from 'ts-serde/primitive'
5つのserde
が準備されています。
それぞれ、JavaScriptのプリミティブ型と文字列型の変換に対応しています。
import type { Serde } from 'ts-serde'
export const string: Serde<string> = {
serialize: String,
deserialize: String
}
import type { Serde } from 'ts-serde'
export const number: Serde<number> = {
serialize: String,
deserialize: Number
}
import type { Serde } from 'ts-serde'
export const bigint: Serde<bigint> = {
serialize: String,
deserialize: BigInt
}
import type { Serde } from 'ts-serde'
export const boolean: Serde<boolean> = {
serialize: String,
deserialize: (str) => str === 'true'
}
import type { Serde } from 'ts-serde'
export const integer: Serde<number> = {
serialize: String,
deserialize: (str) => {
const n = parseInt(str)
return isNaN(n) ? 0 : n
}
}
列挙型(Enums)
特定の値だけを変換したいユースケースもあるでしょう。
そんな時は列挙型(Enums)が使えます。
import { enums } from 'ts-serde/object'
// 列挙したい値を配列で定義する
const e = enums(['foo', 'bar', 'baz'])
// 'foo'を表示
console.log(e.serialize('foo'))
// 'bar'を表示
console.log(e.deserialize('bar'))
// 一覧にないのでエラーをスロー
e.deserialize('qux')
また、第二引数でフォールバック値を指定することで、リスト外の値が入力された際に、その値を代わりに使用します。
import { enums } from 'ts-serde/object'
const e = enums(['foo', 'bar', 'baz'], 'fallback')
// 'fallback'を表示
console.log(e.deserialize('qux'))
オブジェクト型
オブジェクト型の変換にはJSON
とdevalue
の2つのメソッドが用意されています。
import { json } from 'ts-serde/object'
const j = json(
(x): x is { key: string } => {
return typeof x === 'object' && !!x && ('key' in x) && typoef x.key === 'string'
}
)
// '{"key":"value"}'を表示
console.log(j.serialize({ key: 'value' }))
// obj = { key: 'value2' }
const obj = j.deserialize('{"key":"value2"}')
// 型が違うのでエラー
j.deserialize('{"key2":"value2"}')
ここで型ガードが面倒だと感じる方はこのライブラリをお勧めします
このライブラリを使うことで、型ガードをこのように簡単に記述できます。
import { json } from 'ts-serde/object'
import { string } from 'typescanner'
const isValid = scanner({
key: string
})
const j = json(isValid)
enums
と同じく、第二引数でフォールバック値も指定できます。
import { json } from 'ts-serde/object'
import { string } from 'typescanner'
const isValid = scanner({
key: string
})
const j = json(isValid, { key: 'fallback' })
// objには{ key: 'fallback' }が格納される
const obj = j.deserialize('{"key2":"value2"}')
devalue
devalue
は同名のライブラリdevalue
を使用してシリアライズ・デシリアライズします。これにより、通常のJSONではサポートされないMap
型やSet
型、Date
型なども扱うことができます。基本的な使い方はJSON
と同じです。
import { devalue } from 'ts-serde/object'
const d = devalue(
(x): x is Set<Date> =>
// ... Type Guard
,
null // fallback value
)
d.serialize(new Set([new Date()]))
// => '[["Set",1],["Date","20XX-01-01T00:00:00.000Z"]]'
d.deserialize('') // => null (fallback value)
まとめ
いかがだったでしょうか。
この記事がアプリケーション内の統一したシリアライズ・デシリアライズ機能の助けになれば幸いです。
またts-serdeについてバグや不明点がありましたらぜひIssueを開いてください。
Discussion