Closed5
using: Explicit Resource Management feature in ECMAScript

そもそも何?
- TypeScript 5.2では、ECMAScriptの新しいExplicit Resource Management(明示的リソース管理)機能がサポート
何ができる
- オブジェクト作成後のクリーンアップ:
Symbol.dispose
- DisposableというGlobal Objectの追加
使い方
-
using
というキーワードとともに宣言された変数は、スコープの終わりにSymbol.dispose
メソッドが自動的に呼び出される- try finallyを明示的に記述する必要がない
-
Symbol.asyncDispose
によって非同期の解放もサポート -
DisposableStack
とAsyncDisposableStack
によって複数リソースのクリーンアップも可能
注意点
- ランタイムでのネイティブサポートがまだなのでコンパイルターゲットを
es2022
以下に設定、ライブラリ設定にesnext
またはesnext.disposable
を含める必要がある
export function doSomeWork() {
using file = new TempFile(".some_temp_file");
if (someCondition()) {
// do some more work...
return;
}
}
実行タイミング
- スコープを抜ける時、リターン時、throw時
- 基本的に実行順はFILO
function loggy(id: string): Disposable {
console.log(`Creating ${id}`);
return {
[Symbol.dispose]() {
console.log(`Disposing ${id}`);
}
}
}
function func() {
using a = loggy("a");
using b = loggy("b");
{
using c = loggy("c");
using d = loggy("d");
}
using e = loggy("e");
return;
// Unreachable.
// Never created, never disposed.
using f = loggy("f");
}
func();
// Creating a
// Creating b
// Creating c
// Creating d
// Disposing d
// Disposing c
// Creating e
// Disposing e
// Disposing b
// Disposing a
-
例外
Symbol.disposeの処理内で例外がthrowされた場合は、ErrorのサブタイプのSuppressedError
が投げられる
最後にthrowされたエラーを保持するsuppressedプロパティと、最近throwされたエラーを保持するerrorプロパティを備えている。 -
軽量に書く
Disposable
を実装したクラスを定義するのは一般的に認知負荷が高く、ライブラリ等の開発以外では抽象化しすぎかもしれない。
なのでDisposableStackを使用した単発のクリーンアップを行うオブジェクトを使うと良い。
function doSomeWork() {
const path = ".some_temp_file";
const file = fs.openSync(path, "w+");
using cleanup = new DisposableStack();
cleanup.defer(() => {
fs.closeSync(file);
fs.unlinkSync(path);
});
// use file...
if (someCondition()) {
// do some more work...
return;
}
// ...
}
- ネイティブのランタイムはまだサポートが薄い(以下のポリフィルが必要)
- `Symbol.dispose`
- `Symbol.asyncDispose`
- `DisposableStack`
- `AsyncDisposableStack`
- `SuppressedError`
サポートされていなくても組み込みのシンボルをポリフィン具することで使用可能
Symbol.dispose ??= Symbol("Symbol.dispose");
Symbol.asyncDispose ??= Symbol("Symbol.asyncDispose");
またコンパイルターゲットの設定も忘れずに
{
"compilerOptions": {
"target": "es2022",
"lib": ["es2022", "esnext.disposable", "dom"]
}
}

-
そもそも何?
一意の値を生成するために使用される特別なデータ型。オブジェクトのプロパティ名の衝突を避けることができる。 -
何ができる
プロパティキーの衝突回避
let sym2 = Symbol("key");
let sym3 = Symbol("key");
sym2 === sym3; // false, symbols are unique
カプセル化
Symbol
プロパティはfor...in
ループやObject.keys()
メソッドでは列挙されない
ウェルノウンシンボル
Symbol.iterator
やSymbol.asyncIterator
などの組み込みSymbol
を使用してカスタマイズができる
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let current = this.start;
let end = this.end;
// ジェネレータ関数を使用してイテレーションロジックを定義
return {
next() {
if (current <= end) {
return { value: current++, done: false };
} else {
return { done: true };
}
}
};
}
}
// Rangeオブジェクトを使用
let range = new Range(1, 3);
for (let num of range) {
console.log(num); // 1, 2, 3 を順に出力
}

各ランタイム上でのusing対応状況
Node.js: v20.4.0
Deno: v1.38
Bun: v1.0.23
Workers
V8 エンジン側でまだサポートされてないので、Wranglerのプレスリリース版を使用するしかない

挙動検証が参考になる

利用例
JS RPC周りでも使われている
このスクラップは2024/05/15にクローズされました
ログインするとコメントできます