🏄
Matsuri-tech Frontend Weekly 2023-08 別冊 TypeScript 5.2

hrdtbs

hrdtbs

2023年8月31日
MFWTypeScript

社内向けに、フロントエンド関連のニュースや業務で発生したQ&A、利用しているライブラリなどの情報を定期的に書いています。

社内の開発部メンバーに向けて直近でリリースされたライブラリなどの情報をMatsuri-tech Frontend Weeklyとしてまとめています。内容は実務や趣味で使えそうなものを中心に扱っており、網羅的ではなく偏りがあります。

この記事では別冊と称して、2023年8月24日にリリースされたTypeScript 5.2内容からいくつかピックアップしてまとめます。 Announcing TypeScript 5.2 - TypeScriptToday we’re excited to announce the release of TypeScript 5.2! If you’re not familiar with TypeScript, it’s a language that builds on top of JavaScript by making it possible to declare and describe types. Writing types in our code allows us to explain intent and have other tools check our code to catch mistakes like typos,Announcing TypeScript 5.2 - TypeScript

# Explicit Resource Management

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

GitHub - tc39/proposal-explicit-resource-management: ECMAScript Explicit Resource ManagementECMAScript Explicit Resource Management. Contribute to tc39/proposal-explicit-resource-management development by creating an account on GitHub.GitHub - tc39/proposal-explicit-resource-management: ECMAScript 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宣言と,[object Object]

クリーンアップ関数が非同期の場合は、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、非破壊的な配列操作を行えるメソッドのサポートがされました。

GitHub - tc39/proposal-change-array-by-copy: Provides additional methods on Array.prototype and TypedArray.prototype to enable changes on the array by returning a new copy of it with the change.Provides additional methods on Array.prototype and TypedArray.prototype to enable changes on the array by returning a new copy of it with the change. - GitHub - tc39/proposal-change-array-by-copy: ...GitHub - tc39/proposal-change-array-by-copy: Provides additional methods on Array.prototype and TypedArray.prototype to enable changes on the array by returning a new copy of it with the change.

// 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)