tips chips

日々の作業で出てきた技術メモの切れ端を置いておくページ

TypeScript 5.2の新機能たち

usingとかDisposableとか

https://devblogs.microsoft.com/typescript/announcing-typescript-5-2-beta/

全部まとめたかったけど気になるやつだけ。

コードは全て上記ページから引用。

using Declarations and Explicit Resource Management

  • usingという宣言が追加された
    • これはESのProposalをTSが先行して実装したもの( proposal )
  • スコープと連動したリソースのクリーンアップ(fileのcloseや一時ファイルの削除など)を行うための方法が追加された)

import * as fs from "fs"; export function doSomeWork() { const path = ".some_temp_file"; const file = fs.openSync(path, "w+"); // use file... // Close the file and delete it. fs.closeSync(file); fs.unlinkSync(path); }

こんな処理がある

一時ファイルを作って何かして、処理の最後で一時ファイルを閉じて、削除する。

export function doSomeWork() { const path = ".some_temp_file"; const file = fs.openSync(path, "w+"); // use file... if (someCondition()) { // do some more work... // Close the file and delete it. fs.closeSync(file); fs.unlinkSync(path); return; } // Close the file and delete it. fs.closeSync(file); fs.unlinkSync(path); }

Early returnすると同じ処理を2回書く必要がある。try/catchで囲んでfinallyで行えば一度で済むがあまり見通しは良くない。

そこでTS5.2では新たに Symbol.disposeというのが追加された。 Symbol.disposeという名前のメソッドを実装しその中でクリーンアップ処理行う。( Symbol.disposeを実装するインタフェースである Disposableも追加されている)

class TempFile implements Disposable { #path: string; #handle: number; constructor(path: string) { this.#path = path; this.#handle = fs.openSync(path, "w+"); } // other methods [Symbol.dispose]() { // Close the file and delete it. fs.closeSync(this.#handle); fs.unlinkSync(this.#path); } }

そしてこのクラスを使うときに新たに追加された usingメソッドを使う

export function doSomeWork() { using file = new TempFile(".some_temp_file"); // use file... if (someCondition()) { // do some more work... return; } }

すると doSomeWorkを抜けたところで自動的に Symbol.disposeが実行される

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; } // ... }

DisposableStackのインスタンスを作り、deferメソッドの引数にクリーンアップ処理を渡していくと同じくスコープを抜けたときに実行してくれる。

他に知っておいた方が良さげなこと

  • disposeの処理は先入れ後出し(FILO)で実行される。Golangのdeferとかと同じ
  • disposeの処理で非同期処理を入れたい時のために Symbol.asyncDispose AsyncDisposable AsyncDisposableStackというのも用意されている

Named and Anonymous Tuple Elements

// ✅ fine - no labels type Pair1<T> = [T, T]; // ✅ fine - all fully labeled type Pair2<T> = [first: T, second: T]; // ❌ previously an error type Pair3<T> = [first: T, T]; // ~ // Tuple members must all have names // or all not have names.
  • tupleの要素に名前が付けられるようになったよ
  • つけるなら全ての要素につけないとダメだよ

Easier Method Usage for Unions of Arrays

declare let array: number[] | string[] array.filter(x => !!x)

上記のコードがTS5.2より前ではエラーになり、TS5.2では通るようになった。

filterやfindなどのメソッドのシグネチャは

filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];

こうなっているので従来Tの型が確定できなかったが、TS5.2ではこういったケースの時に (number | string)[]として扱ってくれるようになる。(配列型・タプル型のみ)

これによってチェックが通るようになる。

この場合返り値の型も (number | string)[]となってしまう点に注意

(個人的にはこのケースで困った試しがあまりないが……)