Skip to content

Symbol キー

すべての登録キーは string または symbol にできます。文字列は、アプリ全体で公開されるサービスに便利です。Symbol は、同一性が重要な場合に役立ちます。

ts
const DB = Symbol('db')
const CACHE = Symbol('cache')

const c = new Container()
  .registerValue('config', { dsn: 'postgres://localhost/app' })
  .registerClass(DB, PgPool, ['config'])
  .registerClass(CACHE, RedisPool, [])
  .registerClass('repo', UserRepo, [DB, CACHE])

c.get(DB)
c.get(CACHE)
c.get('repo')

Symbol を使うべきとき

パターントークン
モジュールローカルなプライベートサービスSymbol('name')
インポートなしで共有される同一性Symbol.for('name')
公称的な型レベルの区別unique symbol 定数

回収可能なプライベートサービスには、ローカルな symbol を使用してください。Symbol.for(name) はグローバルな symbol レジストリに格納され、決してガベージコレクションされません。

Lazy コンパニオン

lazy コンパニオンのキーも symbol にできます:

ts
const DB = Symbol('db')
const DB_LAZY = Symbol('dbLazy')

const c = new Container()
  .registerClass(DB, PgPool, [], 'singleton', DB_LAZY)

c.get(DB_LAZY).get()

主キーとコンパニオンキーは、同じ種類である必要はありません。

衝突検査では string と symbol の領域を分けて扱います。広い string キーは symbol キーだけを含むグラフへ追加できますが、string キーが一つでも存在すると TypeScript が拒否します。ランタイム値が既存登録を指す可能性があるためです。union キーでは、すべての候補が新しいキーでなければなりません。lazyKey はグラフと主キーの両方に対して検査されます。

同じ値の形

unique symbol はキーに名目的な識別性を与えますが、コンストラクターの構造が同じ 2 つの値までは区別しません。意味上の順序を名目的に扱う必要がある場合は、値の契約をブランド化します。

ts
type PrimaryDsn = string & { readonly __brand: 'primary' }
type ReplicaDsn = string & { readonly __brand: 'replica' }

class Queries {
  constructor(readonly primary: PrimaryDsn, readonly replica: ReplicaDsn) {}
}