O hirunewani blog

Frontend Weekly 2023-08-24 TypeScript 5.2

Created at

TypeScript 5.2のExplicit Resource Management、名前付きタプル要素と匿名タプル要素の混合、配列のユニオン型に対するメソッドの改善、非破壊的な配列メソッドのサポートについて紹介する。

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

Explicit Resource Management

ECMAScript Stage 3 proposalのExplicit Resource Managementが先行実装されました。

https://github.com/tc39/proposal-explicit-resource-management

Explicit Resource Managementのメリット

  • リソース (メモリ、I/Oなど)の明示的な解放
  • try-finallyなどを利用した場合と比べて、安全で実装が容易なリソース管理
  • リソースを扱うAPIのインターフェース統一
    • NodeJS APIでは、http.Serverはclose、stream.Readableはdestroyなどリソースを解放するメソッドに統一感がない

using宣言とSymbol.disposeメソッド

using宣言は、何らかの理由でスコープを脱出する際にSymbol.disposeメソッドを実行します。Pythonのwith文やJavaのtry-with-resources文、C#のusing宣言に似ています。

const getResource = (label: string) => {
	return {
		[Symbol.dispose]: {
			console.log(`Disposed ${label}!`)
		}
	}
}

{
	using resource = getResource("fish")
} // Disposed fish!

using 宣言のクリーンアップはスタックのように振る舞います。

const race = () => {
  using turtle = getResource("turtle");
  using rabbit = getResource("rabbit");
};
race();
// Dispose rabbit!
// Dispose turtle!

await using宣言とSymbol.asyncDisposeメソッド

クリーンアップ関数が非同期の場合は、Symbol.asyncDispose を利用します。

const getResource = (label: string) => {
	return {
		async [Symbol.asyncDispose]: {
			await sleep(1000);
			console.log(`Disposed ${label}!`)
		}
	}
}

{
	await using resource = getResource("fish")
} // Disposed fish!

DisposableStackとdefer()メソッド

DisposableStackとdeferを利用すれば、Symbol.disposeを登録することなくコールバックを登録することが出来ます。Go言語などのdeferをイメージしてもいいですし、Symbol.disposeの場合と同様に振る舞います。

using cleanup = new DisposableStack();
cleanup.defer(() => {
  console.log("Cleanup!");
});

非同期なクリーンアップ関数を実行する場合は、DisposableStackの代わりにAsyncDisposableStackを利用します。

await using stack = new AsyncDisposableStack();
stack.defer(async () => {
  await someAsyncCleanupOperaiton();
});

Explicit Resource Managementを利用する

利用したい場合は、“esnext.disposable”をlibに設定する必要があります。

{
  "compilerOptions": {
    "target": "es2022",
    "lib": ["es2022", "esnext.disposable", "dom"]
  }
}

名前付きタプル要素と匿名タプル要素の混合

タプルに名前付き要素と匿名要素を混ぜることが出来るようになりました。

type Anonymous = [T, T];

// 名前付きタプル
type Named = [first: T, second: T];

// TS 5.2でサポートされた記述
type Merged = [...Anonymous, ...Named];

// TS 5.2でサポートされた記述
type TwoOrMore = [fiest: T, second: T, ...[]];

配列のユニオン型に対するメソッドの改善

今までfilterやreduce、findなどのメソッドを配列のユニオン型に対して呼ぶと、”配列の要素に互換性がないためメソッドを呼べない!”というエラーが吐かれていました。

const array: string[] | number[] = [];

array.filter(x => !!x);

今後は、このようなケースでもユニオン型の要素を持つ配列にフォールバックされるためエラーが吐かれなくなります。しかしstring[] | number[](string | number)[] のように解釈されるようになる点には注意が必要です。

array.filter((x: string | number) => {
  return !!x;
});

非破壊的な配列メソッドのサポート

ES2023の新機能であるChange Array by copy、非破壊的な配列操作を行えるメソッドのサポートがされました。

https://github.com/tc39/proposal-change-array-by-copy

// myArray.reverse()
myArray.toReversed()

// myArray.sort((a, b) => ...)
myArray.toSorted((a, b) => ...)

// myArray.splice(start, deleteCount, ...items)
myArray.toSpliced(start, deleteCount, ...items)

// myArray[index] = updatedValue
myArray.with(index, updatedValue)