mongolyyのブログ

開発(Javascript, Typescript, React, Next.js)や開発手法(スクラム, アジャイル)、勉強したことについて色々書ければと。

TypeScriptで共通なstaticプロパティを定義する

最初にやろうとしたこと

interfaceでstaticプロパティを宣言して、その実装クラスで初期化しようとしました。

interface MoneyIF {
  static readonly CurrencyUnit: string
  readonly amount: number
}

class JPY implements MoneyIF {
  static readonly CurrencyUnit: string = 'Yen'
  readonly amount: number

  constructor(amount: number) {
    this.amount = amount
  }
}

class USD implements MoneyIF {
  static readonly CurrencyUnit: string = 'Dollar'
  readonly amount: number

  constructor(amount: number) {
    this.amount = amount
  }
}

が、残念ながらinterfaceで次の表示が、、

// static' modifier cannot appear on a type member.

oh...

abstract classで表現してみる

github.com

を中心に議論されていますが、現在のバージョンでは、interfaceでstaticプロパティ、メソッドは定義できません。
まだ結論は出ていないようですが、abstract classで宣言する人たちが一定数いることがわかり、手軽に実装できそうでもあったのでその方針で実装してみました。

abstract class MoneyBase {
  static readonly CurrencyUnit: string
  readonly amount: number

  protected constructor(amount: number) {
      this.amount = amount
  }
}

class JPY extends MoneyBase {
  static readonly CurrencyUnit: string = 'Yen'

  constructor(amount: number) {
    super(amount)
  }
}

class USD extends MoneyBase {
  static readonly CurrencyUnit: string = 'Dollar'
 
  constructor(amount: number) {
    super(amount)
  }
}

const usd: MoneyBase = new USD(100)
console.log(usd.amount)
// 100
console.log(USD.CurrencyUnit)
// "Dollar"

いい感じに扱えそうです。
ただここで新たな問題が、、

console.log(usd.CurrencyUnit)
// Property 'CurrencyUnit' does not exist on type 'MoneyBase'. Did you mean to access the static member 'MoneyBase.CurrencyUnit' instead?

インスタンス経由ではstaticプロパティにアクセスできないのです。。

インスタンス経由でstaticプロパティにアクセスする

ちょっと調べてみたところ、いい感じの方法がありました。

stackoverflow.com

this.constructor 経由でstaticプロパティを呼び出す関数を実装しておく方法です。
実装はこんな感じになります。

abstract class MoneyBase {
  static readonly CurrencyUnit: string
  readonly amount: number

  protected constructor(amount: number) {
      this.amount = amount
  }

  get CurrencyUnit() {
      return (this.constructor as typeof MoneyBase).CurrencyUnit
  }
}

class JPY extends MoneyBase {
  static readonly CurrencyUnit: string = 'Yen'

  constructor(amount: number) {
    super(amount)
  }
}

class USD extends MoneyBase {
  static readonly CurrencyUnit: string = 'Dollar'
 
  constructor(amount: number) {
    super(amount)
  }
}

const usd: MoneyBase = new USD(100)

console.log(usd.CurrencyUnit)
// "Dollar"
console.log(USD.CurrencyUnit)
// "Dollar"

インスタンス経由からも、クラスからも無事staticプロパティを呼び出すことに成功しました!

おわりに

議論の余地はありそうではありますが、とりあえずは実装できました。
こうやったらもっと良いよ!とかあれば、教えていただけると嬉しいです!