🤖

TypeScriptにおける値オブジェクトの表現

2022/04/14に公開

メールアドレスが以下のルールを持つとすると、ルールに当てはまらない場合に例外を発生させることができます。

  • 値の型がStringである。
  • 値の最後が@int.dayである。

それぞれのルールをコードにする

メールアドレスは以下のように表現できます。

const email = "xxx@int.day"

システムではこのメールアドレスを検査する必要があります。何故なら、変数にはどんな値が入っているのか分からないからです。

例えばメールアドレスは以下のようなルールを満たしています。

  • 型がstringである。
  • 値に@マークが含まれる。

これらのルールを満たさない時、例外を発生させることができます。

// 型確認
if (  !email.includes("@") ) {
  throw new Error("エラー")
}
if (typeof email !== "string") {
  throw new Error("エラー")
}

zodを使って短く表現する

https://github.com/colinhacks/zod#what-is-zod
zodを使うと短く表現できます。
parse関数にemailを渡している、もしこの値がemailじゃなければ例外が発生します。

import { z } from "zod"

const zValue = z.string().email()

zValue.parse(email)

Emailを値オブジェクトとして表現する

メールアドレスはclassで以下のように表現できます。

export class Email {
  constructor(public value: string) {
  }
}

メールアドレスのインスタンスを生成して使います。
https://qiita.com/nmta/items/696eeab1d6b96e3bfc1a
emailの引数はconstructorの型を見ればわかります。

const email = new Email("xxx@int.day")
email.value // xxx@int.day

しかし、メールアドレスの以外の文字列でもインスタンスを生成出来てしまいます。
そこで、constructorで例外を発生させます。

export class Email {
  constructor(public value: z.infer<typeof zValue>) {
    zValue.parse(value)
  }
}

メールアドレスじゃない文字列でインスタンスを生成した場合、例外が発生します。

テストをする

https://jestjs.io/ja/
この二つをテストします。

  • インスタンスを作成する
  • メールアドレスでないと例外が発生する
    jestを使ったらこのようにテストできます。
import { Email } from "integrations/domain"

describe("Email", () => {
  test("インスタンスを作成する", () => {
    const email = new Email("xxx@int.day")

    expect(email.value).toBe("xxx@int.day")
  })

  test("メールアドレスでないと例外が発生する", () => {
    const email = () => new Email("foo")

    expect(email).toThrowError()
  })
}

Discussion