TypeScriptでモデルの型定義を良い感じに管理したい。

効率良くTypeScriptでドメインモデルの型定義を管理したい。

例えばここに、以下のようなツイッターのような投稿を模したモデルがあるとする。

type Post = {
  id: number,
  content: string
};

ふむ。特筆すべきものはない。 ではこれをモジュールとして型だけexportすることにする。 一文増えた。

type Post = {
  id: number,
  content: string
};

export = Post

ではこの型定義を利用した利用シーンを考える。 addPostという投稿を追加する関数を例で上げてみる。

import * as Post from "./model/post"

const addPost=(post:Post)=>{/* some implementation */}

...

ふむ。良さそうに見える。

ではaddPostを利用するシーンに移ってみる。 ここではテキストエリアに書かれた内容をonClick時に投稿するシーンを想定してみる。 一緒に連番でIdを生成するような関数を想定する。

import {addPost} from "./addPost"

let idCounter=0
const generateId = () => ++idCounter

const onClick=(content:stirng) => {
  const id=generateId()
  addPost({id, content})
}

はて、contentはstringになってしまい、無味乾燥な型になっている。 このstringはなんのstringだっけ?どんな意味を本来持っているんだっけ?ということになりうる。

ここでPostの型定義をしたモジュールに対して色を加えてみようと思う。 次のようなコードだ。

type Post = {
  id: number,
  content: string
};

declare namespace Post{
  export type Id = Post["id"] 
  export type Content = Post["content"] 
}

export = Post

ちょっと面倒な感じがする。 では、先ほどの無味乾燥なソースに手を加えてみようと思う。

import {addPost} from "./addPost"
import * as Post from "./model/post"

let idCounter=0
const generateId:(() => Post.Id) = () => ++idCounter

const onClick=(content: Post.Content) => {
  const id=generateId()
  addPost({id, content})
}

IdやContentの型がコードに表れており、カラフルなコードになったように思う。

さて、ここでリファクタリングのことを考える。 VSCodeのF2で出来るただのRenameのことである。

ここでPostのidの名前変えたくなった。screenIdにしたい。 F2でコードを変えるとどうなるか。

次のようになった。(まとめて書く)

type Post = {
  screenId: number,
  content: string
};

declare namespace Post{
  export type Id = Post["id"] // ここにエラーが発生する。
  export type Content = Post["content"] 
}

export = Post

// ....

import {addPost} from "./addPost"
import * as Post from "./model/post"

let idCounter=0
const generateId:(() => Post.Id) = () => ++idCounter

const onClick=(content: Post.Content) => {
  const id=generateId() // ここにエラーが発生する。
  addPost({screenId, content})
}

コメントで示したところはエラーが発生しており 手で編集する必要がある箇所が2か所発生する。

ここでは修正方法についてはそこまで難しいわけではないので解説しないが Id用の型があるおかげで、ある程度、楽に型を柔軟にメンテしやすくなるように思う。 他にいい方法があれば誰か教えていただければと思う。

Happy TypeScripting!