タグアーカイブ プログラミング

TypeScript 7.0 RCリリース、Go実装でコンパイル速度10倍に

TypeScript 7.0 RCリリース、Go実装でコンパイル速度10倍に

TypeScript 7.0 RCの概要とインストール方法

TypeScript 7.0 RCの概要とインストール方法

TypeScript 7.0 RCの最大の変更点は、コンパイラそのものがGo言語で再実装されたことにある。これまで約10年にわたってTypeScript自身(セルフホスト)で書かれJavaScriptにトランスパイルされてきたコードベースを、Goに移植し直した。ネイティブコードの実行速度と共有メモリによる並列処理によって、コンパイル速度はTypeScript 6.0と比較して約10倍高速化した。

パッケージのインストールは従来と変わらずnpmから行える。以下のコマンドでRelease Candidateを取得し、すぐに試せる。

npm install -D typescript@rc

インストール後は npx tsc --version でバージョンを確認できる。7.0.1-rc が表示されれば準備完了だ。コマンドラインの挙動はTypeScript 6.0と変わらず、同じ型チェック結果が得られる。エディタで試すには、VS Code向けに提供されている TypeScript Native Preview 拡張機能 をインストールするとよい。この拡張機能はLanguage Server Protocol(LSP)に基づいており、Copilot CLIを含む多くのエディタで動作する。

既存のTypeScript 6.0との共存

TypeScript 7.0 RCは安定版に近いが、プログラム向けの正式なAPIは少なくとも数か月先のバージョン7.1まで提供されない。このため、TypeScriptチームは6.0と7.0を並行して使い分けられる仕組みを用意した。互換パッケージ @typescript/typescript6 を導入すると、tsc6 という別名のバイナリが利用可能になる。従来のツールチェーンは6.0に固定しつつ、開発中の7.0を試す構成が取れる。

npm install -D typescript@npm:@typescript/typescript6
npm install -D typescript-7@npm:typescript@rc

これにより tsc は7.0を指し、tsc6 は6.0を指す。typescript-eslintのようなピア依存関係を持つツールでも問題なく使い分けられる。ナイトリービルドは現在 @typescript/native-preview パッケージで提供されており、バイナリ名は tsgo となっている。安定版リリース後は typescript パッケージに統合される予定だ。

単一スレッド動作と並列化の制御

TypeScript 7.0では、解析(パース)、型チェック、出力(エミット)の各段階が並列化されている。解析と出力はファイルごとに独立して処理できるため、大規模なコードベースほど効率よくスケールする。一方、型チェックはファイル間の依存関係が複雑で、完全に独立して動かすと計算の重複やメモリ消費が増える。この課題に対処するため、7.0は 固定数の型チェッカーワーカー を立ち上げる仕組みを採用した。デフォルトのワーカー数は4で、--checkers フラグで変更できる。

CIランナーのようにCPUコア数やメモリが限られた環境では、ワーカー数を減らすことでオーバーヘッドを抑えられる。稀に、--checkers の値によって型チェック結果にわずかな順序依存の差異が出る場合がある。チーム間で同じ結果を得るには、明示的にワーカー数を固定しておくのが安全だ。

従来のシングルスレッド処理と 7.0 の並列処理の比較
従来(TypeScript 6.0)シングルスレッド
パース ファイルA 型チェック ファイルA 出力 ファイルA
全ファイルを 1 スレッドで逐次処理するため、コードベースが大きいと待ち時間が線形に増加する
TypeScript 7.0 の並列処理
パース ファイルA パース ファイルB (同時実行)
型チェッカー 1 担当範囲内のファイル群 型チェッカー 2 別範囲のファイル群
出力 ファイルA 出力 ファイルB
複数スレッドが並列動作し、全体のビルド時間が大幅に短縮される
パース(解析)   型チェック   出力

このデモで示したように、TypeScript 7.0は複数のスレッドを活用して処理を同時に進める。単一スレッドに制限したい場合は --singleThreaded フラグを指定すると、すべての処理を1スレッドで行える。デバッグやパフォーマンス比較に役立つモードだ。

プロジェクト参照ビルドの並列化

TypeScript 7.0はプロジェクト内の並列化に加え、複数のプロジェクト参照を同時にビルドできるようになった。新しい --builders フラグで、並列実行するプロジェクトビルダーの数を制御する。モノレポ構成で多数のプロジェクトを抱える開発環境では、全体のビルド時間をさらに短縮できる可能性が高い。

注意点として、--builders の数と --checkers の数は乗算で効いてくる。たとえば --builders 4 --checkers 4 と指定すると、最大で16個の型チェッカーが同時に動作し、マシンのリソースを圧迫する場合がある。CIランナーやローカル環境のコア数、メモリ容量に応じて適切なバランスを探ることが重要だ。--builders の数値を変えても型チェック結果自体は変わらないが、プロジェクト間の依存グラフがボトルネックになるため、すべてのケースでリニアに速くなるわけではない。

Goへの移植がもたらす互換性と安定性

Goへの移植がもたらす互換性と安定性

今回の移植は「スクラッチからの書き直し」ではなく、既存のTypeScript実装を忠実にGoへ移植する手法が取られた。型チェックのロジックはTypeScript 6.0と構造的に同一であり、これまで蓄積してきた膨大なテストスイートをすべてパスしている。Microsoft社内外の数百万行に及ぶコードベースで実際に使われており、高い互換性と安定性を維持している。

Bloomberg、Canva、Figma、Google、Linear、Miro、Notion、Slack、Vercelなど多くの企業がプレリリース版をテストし、ビルド時間の大幅な短縮と軽快な編集体験を報告している。TypeScriptチームはこれらのフィードバックを受け、リリース候補の品質に自信を持っている。

テンプレートリテラル型のUnicode対応改善

TypeScript 7.0では、テンプレートリテラル型におけるUnicodeコードポイントの扱いがより直感的になった。これまではJavaScriptのUTF-16インデックスに従い、サロゲートペアの分割が発生していた。たとえば "😀abc" をテンプレートリテラル型で推論すると、["\ud83d", "\ude00abc"] のように分割されることがあり、意味的に正しくない文字列リテラル型が生成される可能性があった。

7.0では for...of ループやスプレッド構文と同様に、"😀" を1つの単位として扱う。これにより、絵文字や一部の多バイト文字を含む文字列操作がより自然になり、意図しない型エラーを防げる。UTF-16コードユニット単位での操作を前提としていた型レベルユーティリティには破壊的変更となるが、ほとんどのケースで開発者の期待に沿う結果になる。

JavaScriptファイルのサポート見直し

TypeScriptはもともと、JSDocコメントや特定のコードパターンを解析することでJavaScriptファイルの型チェックをサポートしてきた。7.0ではこの動作をTypeScriptファイル(.ts)の解析ロジックとより一貫性のある形に再設計している。一部の旧来のパターンやJSDocタグの解釈が変更され、より厳密な型の扱いが求められるようになった。

たとえば、値が期待される箇所で直接型として値を使う記法は許容されず、typeof someValue と記述する必要がある。@enum タグは特別扱いされない。単独の ? を型として使うこともできず、代わりに any を使う。Closureスタイルの関数シンタックスもサポート対象外となった。詳細な変更点は公式の CHANGES.md ファイルにまとめられている。

改善されたwatchモードとエディタ体験

改善されたwatchモードとエディタ体験

TypeScript 7.0の --watch モードは、ファイル監視の基盤が全面的に刷新された。新しいファイルウォッチャーは、バンドラのParcelが採用している @parcel/watcher をGoへ移植したものだ。ParcelのウォッチャーはC++で実装されており、ビルドに完全なC++ツールチェーンが必要だったが、今回の移植では最小限のアセンブリシムを併用することで、Goのみで完結する形に仕上げられた。

この移植は、C++からGoへの直接的な翻訳から始まり、最終的にはGoらしいコードへと洗練された。テストスイートも移植され、プラットフォームを問わず安定して動作する。従来のポーリングベースのファイル監視は、大規模プロジェクトで node_modules 以下のファイル変更を監視する際に計算負荷が高かったが、新しいウォッチャーはリソース消費を大幅に抑えている。TypeScriptチームは、VS Codeでの長年の使用実績を持つParcelのウォッチャーをGoでも利用可能にしたことで、エディタとCLIの両方で一貫した高速なファイル監視を実現した。

エディタでの進化とLSP対応

エディタ体験も大きく向上している。VS Code向けのTypeScript Native Preview拡張機能は、LSP(Language Server Protocol)上に構築されており、複数スレッドを活用してリクエストを並列処理する。自動インポート、ホバー情報の展開、インラインヒント、コードレンズ、ソース定義への移動、JSXのリンク編集やタグ補完など、多くの機能が追加された。ベータ版で不足していたセマンティックハイライトや「インポートの並び替え」「未使用インポートの削除」といった機能もRCで組み込まれている。

TypeScriptチームの分析によれば、言語サーバーの失敗コマンド数はTypeScript 6.0と比較して20分の1以下に削減された。GitHub上の主要なTypeScriptおよびJavaScriptコードベースを使ったファズテストを通じて、品質の高さが裏付けられている。

TypeScript 6.0からの移行で注意すべき点

TypeScript 6.0からの移行で注意すべき点

TypeScript 7.0は6.0の型チェック動作と互換性を持つが、6.0で導入された新しいデフォルトや非推奨機能はそのままハードエラーとして扱われる。6.0から7.0への移行をスムーズに進めるために、まず6.0を導入し、非推奨フラグや新しい設定にコードベースを適応させておくことが推奨されている。

以下に主なデフォルト変更点をまとめる。

  • strict がデフォルトで true になる
  • module のデフォルトが esnext になる
  • targetesnext 直前の安定版ECMAScriptバージョンを指す
  • noUncheckedSideEffectImports がデフォルトで true になる
  • libReplacement がデフォルトで false になる
  • stableTypeOrdering がデフォルトで true になり、無効化できない
  • rootDir がデフォルトで ./ になり、内部のソースディレクトリを明示的に設定する必要がある
  • types のデフォルトが [] になり、以前の動作に戻すには ["*"] を指定する

rootDir の変更は、tsconfig.jsonsrc のようなディレクトリの外にあるプロジェクトで影響が大きい。include./src を指定しつつ、compilerOptions.rootDir./src を追加すれば、ディレクトリ構造を維持できる。また、非推奨からハードエラーになった項目として、target: es5module: amd, umd, systemjs, none のサポート終了、moduleResolution: node/node10/classic の非サポート化、alwaysStrict の強制などが含まれる。詳細はTypeScript 6.0のリリースブログでも確認できる。

今後のロードマップと安定版リリース

今後のロードマップと安定版リリース

TypeScriptチームは、RCの公開から約1か月以内にTypeScript 7.0の安定版をリリースする計画を立てている。今後はリリース調整やロジスティクス、報告されたリグレッションの修正に注力し、7.1でのAPI機能の拡充にも取り組む。

実際のプロジェクトでTypeScript 7.0を試し、問題があればGitHubの microsoft/typescript-go リポジトリ でフィードバックを送ることが期待されている。TypeScriptチームは、コミュニティのテスト参加によって安定版を万全の状態で届けたいと考えている。フィードバックは、BlueskyやMastodon、Twitterでも受け付けている。

この記事のポイント

  • TypeScript 7.0 RCはGo言語で再実装され、コンパイル速度が最大約10倍に向上した
  • 既存のTypeScript 6.0と並行して使い分けられる共存パッケージが提供されている
  • 並列化機能(–checkers、–builders)により大規模プロジェクトのビルド時間を短縮
  • watchモードの刷新とエディタのLSP対応強化で、開発体験全体が高速化された
  • 6.0からの移行には、非推奨機能のハードエラー化やデフォルト設定の変更に注意が必要
JavaScriptの分割代入をマスターする——コードを劇的に短く、読みやすくするテクニック

JavaScriptの分割代入をマスターする——コードを劇的に短く、読みやすくするテクニック

JavaScriptのコードを記述する際、配列やオブジェクトから特定の値を取り出して変数に割り当てる作業は日常的に発生する。かつてはインデックス番号やプロパティ名を一つずつ指定して代入していたが、現在のモダンなJavaScriptでは「分割代入(Destructuring Assignment)」という強力な構文が標準となっている。

分割代入を使いこなすことで、コードの行数を大幅に削減できるだけでなく、データの構造を直感的に把握しやすくなる。この記事では、JavaScript教育の専門家であるマット・マーキス氏とアンディ・ベル氏の知見を基に、分割代入の基礎から応用、そして実務で役立つテクニックまでを詳しく解説していく。

単なる構文の紹介にとどまらず、なぜこの手法が推奨されるのか、どのような場面で真価を発揮するのかという背景についても掘り下げていく。この記事を読み終える頃には、複雑なデータ構造を自由自在に解体し、スマートなコードを書くためのスキルが身についているはずだ。

分割代入とは何か——冗長なコードからの脱却

分割代入とは何か——冗長なコードからの脱却

分割代入とは、配列の要素やオブジェクトのプロパティを抽出し、それらを個別の変数として定義するための簡潔な構文だ。2015年に登場したES6(ECMAScript 2015)で導入されて以来、フロントエンド開発において欠かせない技術となっている。

従来の代入方法との比較

分割代入が導入される以前、配列から値を取り出すには以下のような記述が必要だった。それぞれの要素に対して、インデックス番号を指定して一つずつ変数に代入していく形式だ。

const theArray = [ false, true, false ];
const firstElement = theArray[0];
const secondElement = theArray[1];
const thirdElement = theArray[2];

この方法は単純で分かりやすいが、要素数が増えるほどコードが冗長になり、書き間違いのリスクも高まる。これに対して、分割代入を用いた記述は驚くほどシンプルになる。

const theArray = [ false, true, false ];
const [ firstElement, secondElement, thirdElement ] = theArray;

わずか1行で、配列の各要素を対応する変数に割り当てることが可能だ。代入演算子(=)の左側にブラケット([])を使う独特の構文だが、右側の配列の構造をそのまま左側に投影していると考えると理解しやすい。

「データの解体」という考え方

分割代入は英語で「Destructuring」と呼ばれるが、これは「構造(Structure)」を「壊す(De-)」という意味を持つ。しかし、元のデータ構造が破壊されるわけではない。元記事の著者であるマーキス氏は、これを「アンパッキング(荷解き)」と表現している。

大きな箱(配列やオブジェクト)の中に詰め込まれた荷物を、必要な場所(変数)へと素早く整理して並べるイメージだ。元の箱の中身はそのまま維持されるため、安心してデータを展開できる。

配列の分割代入——順序に基づいた展開

配列の分割代入——順序に基づいた展開

配列はインデックス(添字)によって管理されるデータの集合であるため、分割代入もその「順序」に基づいて行われる。ここでは、基本操作から特定の要素を読み飛ばすテクニックまでを見ていく。

基本の構文と要素のスキップ

配列の分割代入では、左辺の変数の位置が、右辺の配列のインデックスに対応する。もし特定の要素が必要ない場合は、カンマだけを残して変数を省略することで、その要素をスキップできる。

const colors = [ "red", "green", "blue" ];
const [ firstColor, , thirdColor ] = colors;

console.log(firstColor); // "red"
console.log(thirdColor); // "blue"

上記の例では、2番目の要素(”green”)を変数に割り当てずに飛ばしている。大量のデータを含む配列から、特定の順序にある値だけを抽出したい場合に非常に有効な手法だ。

1
SKIP
3

配列の2番目を飛ばして1番目と3番目だけを抽出する視覚的イメージ。

このデモのように、分割代入は「必要なスロットだけを確保する」という柔軟な使い方ができる。

デフォルト値の設定

抽出対象の配列に要素が存在しない場合や、値が `undefined` である場合に備えて、デフォルト値を設定することも可能だ。これにより、予期せぬエラーや `undefined` によるバグを防ぐことができる。

const settings = [ "dark" ];
const [ theme, fontSize = "16px" ] = settings;

console.log(theme);    // "dark"
console.log(fontSize); // "16px"(配列に2番目の要素がないためデフォルト値が適用される)

この機能は、設定オブジェクトやAPIレスポンスの処理において、値が欠落している可能性がある場合に極めて重宝する。

オブジェクトの分割代入——キーによる柔軟な抽出

オブジェクトの分割代入——キーによる柔軟な抽出

配列が「順序」に依存するのに対し、オブジェクトの分割代入は「キー(プロパティ名)」に依存する。データの順序を気にする必要がないため、より直感的に必要な情報を指定できるのが特徴だ。

プロパティ名と変数名の一致

最もシンプルな形は、オブジェクトのプロパティ名と同じ名前の変数を用意する方法だ。波括弧({})を使用して記述する。

const user = {
  name: "Taro",
  age: 30,
  job: "Developer"
};

const { name, job } = user;

console.log(name); // "Taro"
console.log(job);  // "Developer"

オブジェクト内に存在するキーを指定すれば、その順番に関わらず値を取り出せる。配列のときのように「何番目の要素か」を数える必要はない。

変数名のカスタマイズ(エイリアス)

プロパティ名とは異なる名前の変数に代入したい場合、コロン(:)を使って新しい名前を指定できる。これを著者のマーキス氏は「一見すると奇妙な構文」と評しているが、慣れると非常に強力な武器になる。

const user = {
  name: "Taro",
  age: 30
};

const { name: userName, age: userAge } = user;

console.log(userName); // "Taro"
console.log(userAge);  // 30

この構文は `プロパティ名: 変数名` という順序で記述する。オブジェクトリテラルの書き方と似ているため混同しやすいが、分割代入においては「右側の名前が新しい変数名になる」という点に注意が必要だ。

name (key) userName (variable)

プロパティ名から新しい変数名へのマッピング構造。

高度なテクニック——ネストした構造と代入パターン

高度なテクニック——ネストした構造と代入パターン

実務で扱うデータは、オブジェクトの中にさらにオブジェクトや配列が含まれる「ネスト(入れ子)」構造であることが多い。分割代入は、こうした複雑なデータに対しても威力を発揮する。

ネストされたオブジェクトの展開

深い階層にある値を取り出す際、かつては `data.user.profile.name` のようにドット記法を繋げて書く必要があった。分割代入を使えば、これを1行で解決できる。

const post = {
  id: 1,
  data: {
    title: "Hello World",
    author: "Mat"
  }
};

const { data: { title, author } } = post;

console.log(title);  // "Hello World"
console.log(author); // "Mat"

この記述では、`data` プロパティを中間変数として作成することなく、その内部にある `title` と `author` を直接変数として抽出している。コードの密度が高まり、情報の関連性が明確になる。

既存の変数への代入(代入パターン)

これまでの例は変数の宣言(constやlet)と同時に分割代入を行っていたが、すでに宣言済みの変数に対して値を代入することもできる。ただし、オブジェクトの場合は構文上の制約がある。

let title, author;
const data = { title: "JS for Everyone", author: "Andy" };

// 波括弧から始めるとブロック文と誤認されるため、丸括弧で囲む必要がある
({ title, author } = data);

console.log(title); // "JS for Everyone"

行の先頭が `{` で始まると、JavaScriptエンジンはそれを変数代入ではなく「コードブロック(if文などの範囲を示すもの)」と解釈してしまう。そのため、全体を `()` で囲むことで、これが式であることを明示する必要がある。これは初学者が陥りやすい落とし穴の一つだ。

Restプロパティ(…)の活用——残りのデータをまとめる

Restプロパティ(...)の活用——残りのデータをまとめる

分割代入の際、特定の数件だけを取り出し、残りのすべての要素を一つの変数にまとめたい場合がある。ここで登場するのが、ドット3つを用いた「Restプロパティ(残余プロパティ)」だ。

配列におけるRest要素

配列の先頭のいくつかの要素を取り出し、残りを新しい配列として保持したい場合に便利だ。

const numbers = [1, 2, 3, 4, 5];
const [first, second, ...others] = numbers;

console.log(first);  // 1
console.log(others); // [3, 4, 5]

この手法は、Reactなどのコンポーネント開発において、特定のpropsだけを抽出し、残りの全ての属性を子要素に渡す(スプレッドする)際によく使われるパターンだ。

APIレスポンスの整理

著者のマーキス氏は、APIから取得した複雑な記事データを処理する例を挙げている。記事の本文(body)とタイトル(title)を抽出しつつ、それ以外の付随するメタデータ(投稿日やカテゴリなど)を一つのオブジェクトにまとめる処理だ。

const apiResponse = {
  id: "post-123",
  body: "Content here...",
  data: {
    title: "Mastering JS",
    pubDate: "2026-03-19",
    category: "Tech"
  }
};

const { body, data: { title, ...metaData } } = apiResponse;

console.log(title);    // "Mastering JS"
console.log(metaData); // { pubDate: "2026-03-19", category: "Tech" }

このように、構造の一部を個別に抽出しながら、残りを「その他」として一括管理できる。これは、将来的にAPIのフィールドが増えたとしても、個別の変数を追加することなく柔軟に対応できる設計に繋がる。

独自の分析:なぜ「分割代入」がモダン開発の要なのか

独自の分析:なぜ「分割代入」がモダン開発の要なのか

分割代入がこれほどまでに普及した理由は、単にタイピング量が減るからだけではない。筆者は、この構文が「データの意図」を明示する役割を果たしているからだと分析している。

可読性と自己文書化

関数の引数などでオブジェクトを受け取る際、関数の先頭で分割代入を行うことで、「この関数はこのオブジェクトのどのプロパティを使用するのか」が一目でわかるようになる。これは、コード自体がドキュメントの役割を果たす「自己文書化」の一助となる。

不変性(Immutability)への意識

分割代入は、元のデータを変更せずに新しい変数を作成する。これはモダンなフロントエンドフレームワーク(ReactやVue.jsなど)が重視する「不変性」の考え方と非常に相性が良い。元のオブジェクトを汚染することなく、必要なデータだけを安全に取り出し、加工するプロセスを自然に促してくれるのだ。

この記事のポイント

  • 分割代入は、配列やオブジェクトから値を抽出し、簡潔に変数へ割り当てる構文である。
  • 配列は「順序」で、オブジェクトは「キー名」で対応する値を特定する。
  • コロン(:)を使えばプロパティ名とは異なる変数名(エイリアス)を指定できる。
  • ネストした深い階層のデータも、1行の構文で一気に展開することが可能だ。
  • Restプロパティ(…)を使えば、抽出されなかった残りのデータをまとめて保持できる。

出典

  • CSS-Tricks「JavaScript for Everyone: Destructuring」(2026年3月19日)