SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

ますます便利になるTypeScript! バージョン3からの変更点と総まとめ

TypeScriptの変更点まとめ ──新しい演算子・トップレベルawait・usingについて

ますます便利になるTypeScript! バージョン3からの変更点と総まとめ 第1回

  • X ポスト
  • このエントリーをはてなブックマークに追加

リソースの解放を自動化できるusing宣言

 本稿最後に紹介するのは、using宣言です。

リソースの解放を伴うコードパターン

 本節で紹介するusingキーワードは、リソースの解放と関連するものです。そこで、まず、リソースの解放に関するコードパターンを紹介します。

 例えば、データベース接続やファイルの読み書き、ネットワークへの接続など、いわゆるリソースと呼ばれるものを利用するコードの場合、それらのリソースオブジェクト、例えば、データベース接続オブジェクトやファイルオブジェクトなどは、処理の最後に解放処理を必ず行う必要があります。これらの解放処理は、リソースオブジェクトにもともと用意されている解放メソッドを実行することが、通常です。そして、この解放処理をどれだけ確実に行えるかが、すなわち、解放メソッドをどれだけ確実に実行できるかが、コーディングの腕の見せ所です。

 例えば、リスト10のような処理コードを考えます。

リスト10:解放処理が行われない可能性があるコードパターン
const connection = new Connection();  // (1)
  :  // (2)
connection.close();  // (3)

 リスト10の(1)は、何かのリソース(例えばデータベース)を利用するためのオブジェクトを生成するコードとします。Connectionクラスをnewすることによって生成したconnectionオブジェクトは、まさにリソースオブジェクトです。以降、リスト10の(2)では、このconnectionを使って、さまざまなデータ処理を行います。そして、このconnectionオブジェクトの解放メソッドがclose()とするならば、(3)のようなコードを最終的に実行する必要があります。

 しかし、もしも(2)でエラーが発生すると、(3)のコードは実行されないことになり、リソースが解放されません。そこで、通常は、リスト11のように、(2)をtryブロックで囲み、(3)をfinallyブロックに記述します。

リスト11:解放処理が確実に行われるコードパターン
const connection = new Connection();  // (1)
try {
  :  // (2)
}
finally {
  connection.close();  // (3)
}

using宣言変数はリソース解放が自動化される

 このようなコードパターンに対応するために導入されたのが、usingキーワードです。このusingは、最新のTypeScriptであるバージョン5.2で導入されたものであり、ECMAScriptでは、現在、ステージ3の段階です。このキーワードを利用すると、リスト11は、リスト12のように記述できます。

リスト12:usingを利用したコードパターン
using connection = new Connection();  // (1)
  :  // (2)

 ポイントは、(1)の変数宣言です。constでもletでもなく、usingで宣言しています。このように、解放処理が必要なリソースオブジェクト変数に対して、usingで宣言するだけで、その後の処理は、(2)のように、tryブロックで囲む必要もなく、close()のような解放処理メソッドの実行も不要です。

リソースオブジェクトへの細工

 ただし、このusingキーワードを利用する場合は、リソースオブジェクトそのものに細工を施す必要があります。それは、Disposableインターフェースを実装したオブジェクトにしておくというものです。このDisposableインターフェースは、リスト13の内容です。

リスト13:Disposableインターフェース
interface Disposable {
  [Symbol.dispose](): void;
}

 Symbol.disposeはSymbolクラスに新たに導入された定数であり、このことから、Disposableの実装オブジェクトでは、それをメソッド名とするメソッドを実装しなければならないということになります。そして、このメソッド内に、リソースの解放処理を記述することになっています。そのため、例えば、リスト12の(1)でのConnectionクラスのコードは、リスト14のようなものになります。

リスト14:Connectionクラス
class Connection implements Disposable {
  :
  constructor() {
    // リソース利用に必要な処理
  }
  :
  close(): void {
    // リソースの解放処理
  }
  [Symbol.dispose](): void {  // (1)
    this.close();  // (2)
  }
}

 リスト14で注目すべきは、(1)と(2)です。インターフェースに定義された通りに(1)のメソッドを実装し、(2)のように、本来、finallyなどで実行していたclose()メソッドを実行します。このコードにより、このクラスをnewしたオブジェクトをusing宣言しておけば、その変数の利用が不要となった際に、自動的に[Symbol.dispose]()メソッドが実行され、close()メソッドが実行されます。結果、自動的にリソースの解放処理が実行されます。

非同期でのリソース解放オブジェクト

 これらのusingキーワードは、非同期での解放処理にも対応しています。例えば、リスト14のclose()メソッドが非同期メソッドとした場合、Connectionクラスはリスト15のようになります。

リスト15:非同期解放のConnectionクラス
class Connection implements AsyncDisposable {  // (1)
  :
  async close(): void {  // (2)
    // リソースの解放処理
  }
  async [Symbol.asyncDispose](): Promise<void> {  // (3)
    await this.close();  // (4)
  }
}

 リスト15では、リソースの解放メソッドclose()が、(2)のようにasyncキーワードが付与されて非同期処理メソッドとなっています。となると、このclose()を実行する場合は、(4)のようにawaitを付与します。その場合は、この解放処理を自動実行するメソッドにも、(3)のようにasyncを付与する必要があります。さらに、メソッド名を、[Symbol.dispose]ではなく、[Symbol.asyncDispose]とします。そして、このメソッドが定義されたインターフェースが、(1)でimplementsしているAsyncDisposableであり、リスト16のコードとなります。

リスト16:AsyncDisposableインターフェース
interface AsyncDisposable {
  [Symbol.asyncDispose](): Promise<void>;
}

非同期でのリソース解放オブジェクトでのusing

 このような非同期で解放を行うリソースオブジェクトを取得する場合のusingの利用方法は、リスト17のようになります。usingの前にawaitをつけるだけです。

リスト17:非同期でのusingを利用したコード
await using connection = new Connection();

リソース解放のみ自動実行できるクラス

 ここまで紹介した方法は、リソースオブジェクトそのものをDisposableインターフェース、あるいは、AsyncDisposableインターフェースを実装したオブジェクトとして、定義できる場合の話です。リソースオブジェクトによっては、そのようなものばかりではありません。その際に便利なのが、DisposableStackクラス、および、その非同期処理用であるAsyncDisposableStackクラスです。このクラスを利用するコードは、リスト18のようになります。

リスト18:DisposableStackを利用したコードパターン
const connection = new Connection();  // (1)
using cleanup = new DisposableStack();  // (2)
cleanup.defer(  // (3)
  () => {
    connection.close();  // (4)
  }
);
  :

 まず、リスト18の(1)のようにリソースオブジェクトを取得します。このリソースオブジェクトには、[Symbol.dispose]()メソッドや[Symbol.asyncDispose]()メソッドが含まれていないとします。すると、usingによる宣言はできません。代わりに、(2)のようにDisposableStack、あるいは、AsyncDisposableStackクラスをnewした変数をusing宣言とします。

 そして、そのDisposableStack/AsyncDisposableStackオブジェクト(リスト18ではcleanup)に対して、(3)のようにdefer()メソッドを実行します。このdefer()メソッドの引数であるアロー関数内で、(4)のようにリソースオブジェクトの解放メソッドを実行します。

 このコードパターンにより、Disposable/AsyncDisposableインターフェースを実装できないリソースオブジェクトでも、using宣言による自動解放が可能となります。

実行環境がまだ未整備

 ただし、このusingに関しては、リリースして間もないこともあり、まともに動作しない環境も多々ある点には注意してください。例えば、本稿執筆時点では最新環境であるNode.jsのバージョン20.0.9、TypeScriptのバージョン5.2.2でも、リスト18のサンプルコードをコンパイルしようとすると、DisposableStackクラスが存在しないというエラーになります。

 また、コンパイルオプションとして、リスト19のような設定を行う必要があります。この点も注意してください。

リスト19:usingを利用する場合のコンパイルオプション
{
  "compilerOptions": {
    "target": "es2022",
    "lib": ["es2022", "esnext.disposable", "dom"]
  }
}

まとめ

 TypeScriptのバージョン5.2までに導入された新機能をテーマごとに紹介する本連載の第1回目はいかがでしたでしょうか。

 今回は、初回ということもあり、概説と、テーマとしては1回分には満たない分量の詰め合わせとして、新しい演算子、トップレベルawait、using宣言を紹介しました。

 次回は、タプルに関するアップデートをテーマに紹介します。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
ますます便利になるTypeScript! バージョン3からの変更点と総まとめ連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 齊藤 新三(サイトウ シンゾウ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。Web系製作会社のシステム部門、SI会社を経てフリーランスとして独立。屋号はSarva(サルヴァ)。HAL大阪の非常勤講師を兼務。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/18805 2023/12/22 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング