JavaScriptでは、クラスをnewして作ったオブジェクトを.toString()しても'[object Object]'が戻り値になる。
この戻り値を、デコレーターを用いて[object クラス名]に変える方法を紹介する。
Object.prototype.toStringを上書きする必要はない
通常、コンストラクタが返す値は、Objectを継承しているので、toStringメソッドが生えている。toStringの結果を変えようとするなら、Object.prototype.toStringをオーバーライドする方法が思いつくが、その必要はない。Object.prototype.toStringが[object Object]を返すのはデフォルトの振る舞いで、この戻り値のObjectの部分だけを変更する仕組みがあるからだ。
Symbol.toStringTagを使う
オブジェクトに[Symbol.toStringTag]プロパティがあると、Object.prototype.toStringは"Object"よりも、そのプロパティの値を尊重してくれる。
class Foo { } // 通常のクラス
class Bar {
[Symbol.toStringTag] = 'Bar'
}
console.log(new Foo().toString()) //=> [object Object]
console.log(new Bar().toString()) //=> [object Bar]
参考: Symbol.toStringTag - JavaScript | MDN
Symbol.toStringTagをデコレータで実現する
クラスごとに[Symbol.toStringTag]を実装するのは面倒なので、デコレータで実現する方法を紹介する。
次のコードのautotagはクラスデコレータの実装で、デコレートされたクラスに[Symbol.toStringTag]プロパティを生やす。
// デコレータの定義
const autotag: ClassDecorator = constructor => {
constructor.prototype[Symbol.toStringTag] = constructor.name
return constructor
}
使い方としては、クラスの手前に@autotagと書くだけ。これで、クラスに[Symbol.toStringTag]を実装したのと同じ効果が得られる。
// 普通のクラス
class Foo { }
// デコレータを使ったクラス
@autotag
class Bar { }
console.log(new Foo().toString()) //=> [object Object]
console.log(new Bar().toString()) //=> [object Bar]