AutoMapper TypeScript を使ってみる
はじめに
TypeScript のインターフェイスはちょっと曲者で、JavaScript にトランスパイルしたときに型情報は消えてしまうため、実行時にたとえば JSON を読み込んだときに型の不一致を起こしてエラーになることがあります。日付型を読み込んだつもりなのに文字列型になっているのはあるあるですね。日付型は Date として扱いたいので、読み込んだ JSON を変換する処理を入れる必要があるのですが、データのコピーを実装するのはなかなか面倒です。C# でいうところの AutoMapper があればいいのにと思ったらちゃんとありました。
AutoMapper TypeScript にはコア ライブラリとストラテジーと呼ばれる機能ごとのライブラリにわかれています。インターフェイスの変換に使用するのは @automapper/pojos
になります。
実行方法
インストール
npm からパッケージをインストールします。
npm install @automapper/core @automapper/pojos
インターフェイスの定義
REST API などで外部から以下のような JSON を取得できるとします。
const json = {
id: 'a84f3c66-25cf-44dd-8dc9-38f89c4d84b2',
name: 'Hikaru Aizawa',
createdAt: '2000-01-01T00:00:00Z',
updatedAt: '2022-11-04T00:00:00Z'
};
この JSON に対応する UserDTO
というインターフェイスを定義します。
interface UserDTO {
id?: string,
name?: string,
createdAt?: string,
updatedAt?: string,
}
ビジネス エンティティとして扱うためのインターフェイスを定義します。createdAt
と updatedAt
が日付型になっています。
interface User {
id?: string,
name?: string,
createdAt?: Date,
updatedAt?: Date,
}
メタデータの定義
先にも述べた通りインターフェイスの情報は JavaScript にしたときに消えてしまうのでメタデータとして型情報を定義してあげる必要があります。
PojosMetadataMap.create<UserDTO>('UserDTO', {
id: String,
name: String,
createdAt: String,
updatedAt: String
});
PojosMetadataMap.create<User>('User', {
id: String,
name: String,
createdAt: Date,
updatedAt: Date
});
マッパーの作成
ストラテジーに pojos
を指定してマッパーを作成します。
const mapper = createMapper({
strategyInitializer: pojos(),
});
UserDTO
から User
へのマッピングを定義します。マッピングの方法は本家の AutoMapper に似ています。変換を行わないメンバーは forMember
を省略することができます。今回は mapFrom
を使っていますが、ignore
(変換を行わない) や fromValue
(固定値を設定する) などの方法が提供されています。詳しくは forMember のドキュメントをご確認ください。
createMap<UserDTO, User>(
mapper,
'UserDTO',
'User',
forMember(_ => _.createdAt, mapFrom(_ => new Date(_.createdAt))),
forMember(_ => _.updatedAt, mapFrom(_ => new Date(_.updatedAt)))
);
オブジェクトの変換
最初に定義した JSON を変換してみます。
const model = mapper.map<UserDTO, User>(json, 'UserDTO', 'User');
ちゃんと日付型になっていることを確認します。
console.log(model.createdAt.toUTCString());
以下のような出力が得られるはずです。
Sat, 01 Jan 2000 00:00:00 GMT
おわりに
今回はエンティティ モデルからビジネス モデルの変換を例に取りましたが、ビジネス モデルとビュー モデルでも同様の問題は発生しますし、バグを生み出しやすいところでもありますので、こうしたライブラリを使っていい感じに手抜きしていきたいですね。
Discussion