3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

TypeScript のための ValueObject レシピ

Last updated at Posted at 2020-09-05

TypeScript で ValueObject を作る時の問題点

thisは宣言されたclassを返す

多くは共通の関数が存在しますので、基底クラスを作成して、継承することを考えると思います。ですが、typescriptのthisは宣言されたclassを返します。
なので、 abstract にするのが良いと思います。 abstract にしたclassは初期化を禁止されます。

また、typescriptでファクトリーを作成するときstaticを採用することでしょう。
その際、function句を使用することでthisを定義することができます。

export abstract class ValueObject<T> {
  constructor(protected value: T) {}
  public get() {
    return this.value;
  }
  public eq<Instance extends ValueObject<T>>(
    this: Instance, 
    valueObject: Instance
  ): boolean {
    return valueObject.get() === this.value;
  }
  public static of = function <T, Instance extends ValueObject<T>>(
    this: new (value: T) => Instance,
    value: ReturnType<Instance['get']>
  ) {
    return new this(value);
  };
}

typescriptはメンバーが同じなら同等に評価する

基底クラスを使用して、価格と金額を作成して、矛盾した代入をしてみると、lintは何も反応してくれません。これは初期化した後にオブジェクトの構造が同じだから起こります。
理想はエラーを出してほしい!

import { ValueObject } from './ValueObject';

/**
 * 価格
 */
class Price extends ValueObject<number> {}

/**
 * 金額
 */
class Amount extends ValueObject<number> {}

const amount: Amount = Price.of(100);

解決 エラーを出してもらう

この記事では、 unique symbol を使用する事で、この問題を解決します。

下記のコードは強引ですが、目的を確実に果たしてくれます。
説明すると、 private にすることで露出させず、class内部で参照がないことでlintがエラーを検出するため、 @ts-ignore を指定して、無視することをしています。Uniqueは再利用可能ですので、テンプレートとしても利用可能です。

import { ValueObject } from './ValueObject';

declare const Unique: unique symbol;

/**
 * 価格
 */
export class Price extends ValueObject<number> {
  // @ts-ignore
  private [Unique]: void;
}

ビルド後に残る?

private [Unique]: void; の部分は定義のみですので、コードには残りません。
下記ビルド後のコード

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const ValueObject_1 = require("./ValueObject");
class Price extends ValueObject_1.ValueObject {}
exports.Price = Price;

Reactのプロジェクトでの話

ReferenceError: Unique is not defined

index.htmlUnique 変数を定義することで回避できる

<script>
  var Unique = undefined;
</script>

メンバーが残ってる

整合性を取るか、無駄をなくすかのトレードオフ。。。

undefined: undefined
value: 100
__proto__: ValueObject
3
3
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
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?