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) {}
}