---
title: "TypeScript Compiler APIの使い方｜AST解析・型取得のコードとTypeScript 7.1の新API"
url: "https://www.issoh.co.jp/tech/details/3351/"
published: 2024-08-20
updated: 2026-11-01
categories: ["TypeScript"]
publisher: "株式会社一創"
---

# TypeScript Compiler APIの使い方｜AST解析・型取得のコードとTypeScript 7.1の新API

TypeScript Compiler API は、tsc コマンドが内部で使っているコンパイラ本体をプログラムから呼び出すための API です。ソースコードを抽象構文木（AST）として読み、型チェッカに型を問い合わせ、書き換えた結果を出力する処理を、コマンドラインを介さずに実装できます。lint ルールの自作、コードの一括変換、型定義からのドキュメント生成といったツールがこの API の上に成り立っています。2026年11月時点では、従来の ts.\* API（typescript 6系）と、TypeScript 7.1 beta で正式な入口になった新 API（typescript/sync）の2系統が並んでいます。本記事では両方のコードを、手元で実行して動いた形で示します。

## まとめ：Compiler APIの全体像と2026年11月時点のバージョン選択

Compiler API の中心は Program・SourceFile・TypeChecker の3つです。Program でプロジェクト全体を表し、そこから各ファイルの AST（SourceFile）と、型を解決する TypeChecker を取り出します。AST を走査してノードを判定し、必要なら TypeChecker に型を尋ね、Printer で書き戻す。この流れは 6系でも 7.1 でも変わりません。

変わったのはパッケージの中身です。npm の typescript は latest が 7.0.2 で、Go で書き直されたこの 7系には `import ts from "typescript"` で呼べる ts.createProgram がありません。7.0.2 では新 API が typescript/unstable/sync という試験用の入口に置かれていましたが、2026年10月20日公開の 7.1.0-beta で unstable が外れ、typescript/sync で createProgram を呼べるようになりました。7.1 の正式版は11月24日の予定です。

| 状況                             | 選ぶ版                | 入口                           |
| ------------------------------ | ------------------ | ---------------------------- |
| ts-morph・typescript-eslint を使う | 各ライブラリの対応版（現行は6系）  | ライブラリ経由                      |
| 既存の解析ツールを保守する                  | 6.0.3 に固定          | 同上                           |
| ts.\* API に依存しない自作ツールを新規に書く    | 7.1.0-beta を固定して試作 | typescript/sync              |
| 7.0.2 の unstable で書いた          | 7.1 への書き換えが必要      | typescript/unstable/sync は廃止 |

**従来の ts.\* API に依存するコードやライブラリを使うなら、6系に留める。**ts-morph は TypeScript を同梱しているので、その場合は同梱版に従います。新 API を選ぶのは、ts.\* API に依存しない自作ツールで、正式版の公開後に書き直す手間を許容できる場合だけです。

## Compiler APIの構成要素と公式資料の位置づけ

### Program・SourceFile・TypeChecker・Printerの分担

Program はコンパイル対象のファイル群とコンパイラオプションをまとめた単位です。SourceFile はファイル1本を表し、その AST のルートノードそのものです。TypeChecker は識別子と型を結びつけ、「この変数の型は何か」「この代入は妥当か」に答えます。書き換えた AST を文字列に戻すのが Printer です。

tsc との差は粒度です。tsc は tsconfig.json の読み込みから型チェック、JavaScript の出力までを一括で実行します。Compiler API なら各段を個別に呼べるので、型チェックだけ走らせる、出力せずに AST だけ取る、といった組み方ができます。

構文だけを見るなら SourceFile で足ります。createSourceFile は単体で AST を作れるため、Program の生成も型解決も要りません。型を扱う処理は必ず Program を経由します。TypeChecker は Program からしか取得できず、Program の生成には依存ファイルの解決が伴い、既定の CompilerHost ならディスクからの読み込みも発生します。構文チェックだけで済む処理を Program 経由で書くと、実行時間が不必要に伸びます。

### CompilerHostによるファイルシステムの差し替え

Program を作るとき、コンパイラはファイルの読み込みやディレクトリの存在確認を CompilerHost というインターフェース越しに行います。createProgram にホストを渡さなければ実ファイルシステムを読む既定のホストが使われ、独自の CompilerHost を渡せばメモリ上の仮想ファイルを対象に型チェックを走らせられます。ブラウザで動く TypeScript プレイグラウンドや、テストコード内で型エラーを検証する仕組みはこの差し替えで成り立っています。7.1 の新 API では、この役割を後述の typescript/fs が担います。

### 公式ドキュメントの所在とバージョン固定の前提

6系までの一次資料は、GitHub の microsoft/TypeScript の Wiki にある [Using the Compiler API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API) です。最小のコンパイラ、変換関数、AST を辿る簡易リンター、型チェッカの使い方などのサンプルが並んでいます。同じ Wiki の Architectural Overview と Compiler Internals は、パーサからバインダ、型チェッカへ至る処理の流れを扱っています。AST のノード構造を手早く確かめたいときは、ソースを貼ると AST を表示する ts-ast-viewer.com が使えます。

このページは2026年8月17日の更新で冒頭に注意書きが入り、「The contents of this page currently describe TypeScript 6.0 and earlier. TypeScript 7.1 will have a completely different API.」と明記されました。インストール手順も「TypeScript >=1.6, <7」を指定しています。それまで冒頭にあった「this is not yet a stable API」という一文は、この更新で削られました。API の形が版ごとに動く点は変わらず、後述のとおり SyntaxKind の数値は 7.0.2 と 7.1.0-beta の間でも動いています。依存するツールでは typescript のバージョンを完全固定にし、更新時は自前のテストで確かめてください。7.1 の新 API には、現時点で Wiki に相当するまとまった資料がなく、[TypeScript 7.1 Beta の公式発表](https://devblogs.microsoft.com/typescript/announcing-typescript-7-1-beta/)とパッケージの型定義ファイルが一次情報になります。

## ts.createProgramによる解析環境の構築（TypeScript 6系）

### インストールとProgram生成の最小コード

まず typescript をバージョン固定で入れます。latest は 7.0.2 なので、従来の API を使うなら版の明示が要ります。npm は既定で package.json に ^6.0.3 と書き込むため、`--save-exact` を付けて版そのものを保存します。

```
npm install --save-exact typescript@6.0.3 @types/node
```

以降のコードは、次の sample.ts と tsconfig.json を置いたディレクトリで動かしました。各コードは .mts で保存し、`node ファイル名.mts` で実行しています。Node.js v26.5.0 は型注釈を取り除いてそのまま実行します。解析コード自体の型チェックは、sample.ts の意図的なエラーと混ざらないよう tsconfig.json を使わず、`npx tsc --ignoreConfig --noEmit --strict --module nodenext --target es2022 --types node ファイル名.mts` で行い、型エラーは出ませんでした。TypeScript 6 以降は tsconfig.json のあるディレクトリでファイルを直接指定すると TS5112 で止まるため、`--ignoreConfig` が要ります。

```
// sample.ts
export function add(a: number, b: number): number {
  return a + b;
}
export const label: string = 42;

// tsconfig.json
{ "compilerOptions": { "strict": true, "target": "es2022", "module": "nodenext", "noEmit": true }, "files": ["sample.ts"] }
```

Program を作り、型チェックの結果を診断（Diagnostic）として取り出すコードは次のとおりです。

```
import ts from "typescript";

const program = ts.createProgram(["sample.ts"], {
  strict: true,
  target: ts.ScriptTarget.ES2022,
  noEmit: true,
});

for (const d of ts.getPreEmitDiagnostics(program)) {
  const message = ts.flattenDiagnosticMessageText(d.messageText, "\n");
  if (d.file && d.start !== undefined) {
    const { line, character } = d.file.getLineAndCharacterOfPosition(d.start);
    console.log(`TS${d.code} ${d.file.fileName}(${line + 1},${character + 1}): ${message}`);
  } else {
    console.log(`TS${d.code} ${message}`);
  }
}
```

createProgram の第1引数はルートファイル名の配列、第2引数は CompilerOptions です。getPreEmitDiagnostics は構文エラーと型エラーをまとめて返し、オプションの誤りのようにファイルに紐づかない診断は file を持たないので、位置なしで表示します。上の sample.ts を対象に実行すると、`TS2322 sample.ts(4,14): Type 'number' is not assignable to type 'string'.` と出力されました。

### diagnostic.messageTextを直接文字列として扱わない理由

messageText は string とは限らず、関連する複数のメッセージが入れ子になった DiagnosticMessageChain になることがあります。「型 A は型 B に代入できません」の下に「プロパティ x の型が違います」がぶら下がるようなケースです。そのまま文字列連結すると \[object Object\] と表示されるため、上のコードのように flattenDiagnosticMessageText で平坦化します。第2引数は入れ子を区切る文字列です。7系の新 API では診断が text という文字列のプロパティで返るので、この処理は要りません。

### tsconfig.jsonを読み込んでProgramへ渡す2段階の手順

実プロジェクトを解析対象にするなら、CompilerOptions を手書きせず tsconfig.json から読みます。JSON を読む readConfigFile と、それを CompilerOptions とファイルリストへ展開する parseJsonConfigFileContent の2段階です。

```
import ts from "typescript";
import path from "node:path";

const configPath = path.resolve("tsconfig.json");
const { config, error } = ts.readConfigFile(configPath, ts.sys.readFile);
if (error) throw new Error(ts.flattenDiagnosticMessageText(error.messageText, "\n"));

const parsed = ts.parseJsonConfigFileContent(config, ts.sys, path.dirname(configPath));
if (parsed.errors.length > 0) {
  throw new Error(parsed.errors.map((e) => ts.flattenDiagnosticMessageText(e.messageText, "\n")).join("\n"));
}

const program = ts.createProgram(parsed.fileNames, parsed.options);
```

readConfigFile は JSON の構文エラーを error で、parseJsonConfigFileContent は不正なオプションや該当ファイルなしを errors で返します。どちらも例外を投げないので、確かめずに進むと原因の分からない空の Program ができます。include・exclude・extends の解決は parseJsonConfigFileContent が担います。この段を省いて tsconfig.json の compilerOptions をそのまま createProgram に渡すと、設定が黙って無視されるのではなく、次の例外で止まります。

```
target is a string value; tsconfig JSON must be parsed with parseJsonSourceFileConfigFileContent or getParsedCommandLineOfConfigFile before passing to createProgram
```

JSON では文字列で書かれる target や module を、コンパイラが数値の列挙型として受け取れないことが原因です。この文面が出たら展開処理の抜けを疑ってください。

## ASTの走査とノード判定

### forEachChildとgetChildrenの使い分け

AST を辿る方法は2つあり、返すものが違います。forEachChild は構文上意味のある子ノードだけを返します。getChildren はトークンまで含めた子ノード列を返し、途中に SyntaxList という中間ノードが挟まります。同じソースファイルの直下を列挙すると、forEachChild は変数文・関数宣言・EndOfFileToken の3つを返し、getChildren は SyntaxList と EndOfFileToken の2つしか返しません。実際の文はその下の階層にあります。

```
import ts from "typescript";

const source = 'const message: string = "hello";\nfunction f(a: number) { return a; }\n';
const sf = ts.createSourceFile("in.ts", source, ts.ScriptTarget.ES2022, true);

ts.forEachChild(sf, (node) => {
  console.log(ts.SyntaxKind[node.kind]);
});
```

6.0.3 で実行すると FirstStatement・FunctionDeclaration・EndOfFileToken の3行が出ます。1行目が VariableStatement でない理由は次項のとおりで、ノードの実体は変数文です。解析ツールでは基本的に forEachChild を選び、getChildren はカンマや括弧の位置まで再現する整形ツールなど、トークン単位の情報が要るときに限ります。createSourceFile の第4引数（setParentNodes）を true にしているのは、false だと各ノードの parent が設定されず親を辿れないためです。

### SyntaxKindの逆引きがFirstStatementを返す問題と数値の変動

SyntaxKind には範囲を示す別名が同じ数値で定義されており、6.0.3 では VariableStatement と FirstStatement がどちらも 244 です。TypeScript の列挙型は逆引きテーブルを後勝ちで作るため、`ts.SyntaxKind[244]` は `"FirstStatement"` を返します。判定は ts.isVariableStatement のような is 系ヘルパーで行い、逆引き文字列は参考表示に留めてください。

7.1.0-beta では同じ VariableStatement の値が 245 に変わっていました（7.0.2 は 244）。ノード種別を数値のまま保存したり比較したりするコードは、版が上がると黙って壊れます。7系の typescript/ast/utils にある formatSyntaxKind は 245 に対して `"VariableStatement"` を返すので、ログ表示にはこちらを使えます。

## TypeCheckerによる型情報の取得

### SymbolとTypeを取り出す手順

宣言の名前から型を知りたいときは、ノードから Symbol を引き、Symbol から Type を引きます。Symbol は宣言された名前に対応し、Type はその名前に付いた型です。式など任意のノードの型だけが欲しいなら、checker.getTypeAtLocation(node) で直接取れます。関数宣言から関数の型を取り出すと次のようになります。

```
import ts from "typescript";

const program = ts.createProgram(["sample.ts"], { strict: true });
const checker = program.getTypeChecker();
const sf = program.getSourceFile("sample.ts");
if (!sf) throw new Error("sample.ts が見つかりません");

ts.forEachChild(sf, (node) => {
  if (!ts.isFunctionDeclaration(node) || !node.name) return;
  const symbol = checker.getSymbolAtLocation(node.name);
  if (!symbol) return;
  const type = checker.getTypeOfSymbolAtLocation(symbol, node);
  console.log(symbol.getName(), checker.typeToString(type));
});
```

sample.ts に `function add(a: number, b: number): number` があると、`add (a: number, b: number) => number` と表示されます。getSymbolAtLocation に渡すのは宣言ノードではなく node.name です。Symbol は名前に紐づくため、FunctionDeclaration ノードを直接渡すと undefined が返ります。

### isTypeAssignableToによる代入可能性の判定

typeToString の結果を文字列比較に使うのは避けてください。型引数の表示揺れやエイリアスの展開有無で結果が変わり、構造的部分型の判定もできません。代入可能かどうかは checker.isTypeAssignableTo、ユニオン型やリテラル型の判別は type.isUnion() や type.isStringLiteral() を使います。typeToString はログとエラーメッセージの生成に限るのが安全です。isTypeAssignableTo は 7.1 の Checker にも同名で残っています。

## Transformerによるコード変換とPrinterでの出力

### visitEachChildによるノードの再帰的な置き換え

コード変換は、AST を訪問しながら新しいノードを返す TransformerFactory を書き、ts.transform に渡します。TypeScript の AST は不変（immutable）として扱う設計なので、既存ノードを書き換えず、置き換え後のノードを factory から作って返します。

```
import ts from "typescript";

const sf = ts.createSourceFile(
  "in.ts",
  'const message = "hello";\n',
  ts.ScriptTarget.ES2022,
  true,
);

const renamer: ts.TransformerFactory<ts.SourceFile> = (context) => (root) => {
  const visit = (node: ts.Node): ts.Node => {
    if (ts.isIdentifier(node) && node.text === "message") {
      return context.factory.createIdentifier("greeting");
    }
    return ts.visitEachChild(node, visit, context);
  };
  return ts.visitNode(root, visit, ts.isSourceFile);
};

const result = ts.transform(sf, [renamer]);
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
console.log(printer.printFile(result.transformed[0]));
result.dispose();
```

実行結果は `const greeting = "hello";` です。visitEachChild は子ノードに visit を適用し、変化があったノードだけを作り直します。変化がなければ元のノードをそのまま返すので、大きなファイルでも全ノードの複製は起きません。

### printFile出力でのフォーマット非保持

Printer は AST から文字列を組み立て直すため、元の書式は完全には保持されず、インデントや空行、コメントの配置が変わることがあります。既存コードの一部だけを書き換えて差分を小さく保ちたい codemod では、変更したノードの位置（getStart と getEnd）を使って元の文字列の該当範囲だけを置換するほうが、レビューしやすい差分になります。

## TypeScript 7.1 betaの新APIによる解析コードの実装

### 6.0.3・7.0.2・7.1.0-betaのエントリポイントの違い

3つの版を同じディレクトリに入れ、package.json の exports と実際の import を確かめました。7系はどちらもルートの `"typescript"` から version と versionMajorMinor しか取れず、`require("typescript").createProgram` は undefined です。

| 版          | Program を作る入口                             | tsconfig の読み込み                              |
| ---------- | ----------------------------------------- | ------------------------------------------- |
| 6.0.3      | ts.createProgram                          | readConfigFile ＋ parseJsonConfigFileContent |
| 7.0.2      | typescript/unstable/sync の updateSnapshot | openProjects に tsconfig のパス                 |
| 7.1.0-beta | typescript/sync の api.createProgram       | api.parseConfigFile                         |

7.1.0-beta の exports は typescript/sync、typescript/async、typescript/fs、typescript/ast、typescript/ast/is、typescript/ast/factory、typescript/ast/visitor などで、unstable を含むパスは1つもありません。公式発表は、sync を「compiler options・type-checker・program の API を持つ同期クライアント」、async をその Promise 版と説明しています。7.0.2 の API クラスが持っていた公開メソッドは parseConfigFile・updateSnapshot・close などに限られていましたが、7.1.0-beta では createProgram・parseCommandLine・createSourceFile・transpileModule・createSnapshot が加わり、updateSnapshot は型定義上 private になりました。

### typescript/syncでtsconfigを読んで型を問い合わせるコード

```
npm install --save-exact typescript@7.1.0-beta @types/node
```

前章の 6系のコードと同じく、診断を出して関数の型を表示する処理を 7.1 の API で書くと次のようになります。Node.js v26.5.0 で .mts のまま実行でき、7.1.0-beta の tsc による型チェックも通りました。

```
import { API } from "typescript/sync";
import { isFunctionDeclaration } from "typescript/ast/is";
import { resolve } from "node:path";

using api = new API();
const config = api.parseConfigFile(resolve("tsconfig.json"));
using program = api.createProgram(config.fileNames, config.options);

for (const d of program.getSemanticDiagnostics()) {
  console.log(`TS${d.code} ${d.fileName}:${d.pos} ${d.text}`);
}

const checker = program.getProject().checker;
const sf = program.getSourceFile(resolve("sample.ts"));
sf?.forEachChild((node) => {
  if (!isFunctionDeclaration(node) || !node.name) return;
  const symbol = checker.getSymbolAtLocation(node.name);
  if (!symbol) return;
  console.log(symbol.name, checker.typeToString(checker.getTypeOfSymbol(symbol)));
});
```

出力は `TS2322 /…/sample.ts:83 Type 'number' is not assignable to type 'string'.` と `add (a: number, b: number) => number` で、このサンプルでは6系と同じ結果です。ただし 6系の getPreEmitDiagnostics が構文・オプションなどの診断もまとめて返すのに対し、この例の getSemanticDiagnostics は型の診断だけです。公式発表の例のように getGlobalDiagnostics・getProgramDiagnostics・getSyntacticDiagnostics と組み合わせると同等の範囲になります。6系との違いは4点あります。TypeChecker は program.getTypeChecker() でなく program.getProject().checker から取ります。子ノードの走査は ts.forEachChild(node, cb) でなく node.forEachChild(cb) です。診断の位置は行・列でなく pos（ファイル先頭からの文字数）で返ります。ファイル名は相対パスでなく絶対パスで渡します。

### usingとdisposeによるGo側メモリの解放

上のコードは const でなく using で宣言しています。7系の API は Go 製のコンパイラを別プロセスで起動し、JavaScript 側はその通信クライアントとして動きます。公式発表によると、API と Program はどちらも Disposable で、使い終わったら解放しないと Go 側でメモリがリークします。解放の方法は、api.close()・program.dispose()・api\[Symbol.dispose\]() を明示的に呼ぶか、using で宣言してスコープを抜けるときに自動で解放させるかのどちらかです。dispose した Program は再利用できません。

短いスクリプトなら Node.js の終了と同時に片付きますが、エディタ拡張や開発サーバーのように同じプロセスで API を何度も作るツールでは、解放漏れがそのまま積み上がります。using が使えない実行環境では try/finally で close を呼んでください。

### ASTの書き換えで変わるfactoryの引数

7.1 にも visitEachChild と factory があり、console.log を logger.info に置き換える変換を書いて api.printer.printFile で出力できました。子ノードを先に変換してから判定するので、引数の中の console.log も置き換わり、console.warn は残ります。この例は識別子の文字列だけで判定しているため、ローカル変数として宣言した console も対象になります。実際の codemod では、TypeChecker で Symbol を引いてグローバルの console かを確かめてください（6系の message から greeting への例も同じく教材用です）。ただし factory 関数の引数が 6系と同じではありません。

```
import { API } from "typescript/sync";
import { NodeFlags, type Node } from "typescript/ast";
import { isCallExpression, isIdentifier, isPropertyAccessExpression, isSourceFile } from "typescript/ast/is";
import { createIdentifier, createPropertyAccessExpression, updateCallExpression } from "typescript/ast/factory";
import { visitEachChild, visitNode } from "typescript/ast/visitor";

using api = new API();
using retained = api.createSourceFile("/virtual/a.ts", 'console.log(console.log("a"));\nconsole.warn("b");\n');

const visit = (node: Node): Node => {
  const visited = visitEachChild(node, visit);
  if (
    isCallExpression(visited) &&
    isPropertyAccessExpression(visited.expression) &&
    isIdentifier(visited.expression.expression) &&
    visited.expression.expression.text === "console" &&
    visited.expression.name.text === "log"
  ) {
    const callee = createPropertyAccessExpression(
      createIdentifier("logger"), undefined, createIdentifier("info"), NodeFlags.None,
    );
    return updateCallExpression(visited, callee, visited.questionDotToken, visited.typeArguments, visited.arguments);
  }
  return visited;
};

console.log(api.printer.printFile(visitNode(retained.sourceFile, visit, isSourceFile)));
// logger.info(logger.info("a"));
// console.warn("b");
```

7.1.0-beta の型定義では、createPropertyAccessExpression が最後の引数に flags（NodeFlags）を必須で取り、updateCallExpression は第3引数に questionDotToken を取ります。6系の書き方のまま updateCallExpression(node, callee, node.typeArguments, node.arguments) と渡すと引数が1つずつずれ、実行時に`Cannot use 'in' operator to search for 'pos' in undefined`で止まりました。visitEachChild も context を取らない2引数です。6系の Transformer を移すときは、factory 呼び出しを1つずつ型定義と突き合わせてください。api.createSourceFile が返すのは AST そのものではなく、sourceFile プロパティを持つ RetainedSourceFile で、これも Disposable です。

### スナップショットと仮想ファイルシステム

7.1 の API の中心にあるのはスナップショットです。公式発表では、ある時点のプロジェクトと Program 一式を表すものとされ、api.createSnapshot で作り、snapshot.update で差分だけ渡して次のスナップショットを得ます。エディタのように入力のたびに型チェックをやり直す処理で、毎回すべてを組み立て直さずに済みます。

ファイルの供給元は typescript/fs で差し替えます。createFileSystem は列挙したファイルだけが存在する完全な仮想ファイルシステム、createFileSystemWithLib はそれに既定のライブラリ（lib.d.ts 群）を読み取り専用で加えたもの、createFileSystemLayer は指定したファイルだけを上書きし残りは下の層に任せるものです。公式発表のサンプルを手元で動かすと、型エラーのある版から修正版へ update した後も、元の Program に診断を問い合わせると1件のエラーが返りました。古いスナップショットは消えずに残ります。ただし発表は、別々のスナップショットから得た program・project・checker を混ぜて使わないよう注意しています。6系の CompilerHost を自作してテスト用の仮想ファイルを流し込んでいた処理は、7.1 ではこの関数で書き直すことになります。

### content mappersによる.vue・.svelteなどの型チェック

7.1 では content mappers という拡張点も入りました。tsconfig.json の contentMappers に拡張子と担当パッケージを登録すると、.vue などのファイルから TypeScript 用の仮想表現を作り、エラー位置を元のファイルへ対応付けます。Vue・Svelte 向けのツール Volar が担っていた仕組みを本体が受け持つ形で、公式発表は Astro・CSS Modules・Ember・MDX・Svelte・Vue で試作または実装があるとしています。コマンドラインで使うには `tsc --runExternalCode` の指定が必要です。content mapper は任意の実行ファイルとして動くためで、VS Code ではワークスペースの信頼と content mapper の許可が求められます。独自拡張子の言語でこの仕組みを使った例として、[TSRXとは？JSXの後継を掲げるTypeScript拡張の構文とReact導入手順](/tech/details/12439/)で tsrx-tsc の設定を扱っています。

## 6系に留めるか7.1へ移るかの判断

### ts-morphとtypescript-eslintの対応状況

ラッパーライブラリとして広く使われている ts-morph は、最新の 28.0.0（2026年4月13日公開）が依存する @ts-morph/common 0.29.0 で TypeScript 6.0.2 を同梱しています。型情報付きの lint を支える @typescript-eslint/typescript-estree は、2026年10月29日時点の最新 8.72.1 でも peerDependencies が typescript を「>=4.8.4 <6.1.0」と定めており、7系は対象外です。これらを挟むツールは、7.1 の正式版が出ても各ライブラリが対応するまで 6系に留まります。TypeScript 7 本体の速度や移行手順は[TypeScript 7（TS7）とは？Go製tscで約10倍速く](/tech/details/10175/)に、5系から6系への移行は[TypeScript 6.0の移行手順と7.0との違い](/tech/details/11066/)にまとめています。

### 7.0.2のunstable APIで書いたコードが7.1で動かない理由

7.0.2 向けに `import { API } from "typescript/unstable/sync"` と書いたコードは、7.1.0-beta を入れると読み込みの時点で ERR\_PACKAGE\_PATH\_NOT\_EXPORTED になり、1行も実行されません。exports から unstable のパスが消えたためです。パスを typescript/sync に直しても、7.0.2 で入口にしていた updateSnapshot は 7.1 で private になったので、createProgram か createSnapshot へ組み替えることになります。「unstable」は名前どおりの意味でした。7.0.2 の API で書いたツールは、ここで一度書き直す前提で扱ってください。

### 7.1正式版の予定と待つべき条件

TypeScript チームの[7.1 Iteration Plan](https://github.com/microsoft/TypeScript/issues/63703)では、RC が2026年11月10日、正式版が11月24日の予定です。ベータは計画上10月6日でしたが、実際の公開は10月20日でした。公式発表の締めくくりも、7.1 では API の仕上がりを優先すると書いています。発表は「パートナーチームと API の使い勝手と性能を詰めている」とも述べており、正式版までにメソッド名や引数が変わる可能性は残ります。

7.1.0-beta で試作を始めるなら、版を固定し、この記事の factory の例のように型定義と突き合わせる単体テストを用意しておくと、RC や正式版での変更をすぐに検出できます。依存ライブラリの対応を待つツールは 6系で運用を続け、6系の更新が出たら自前のテストで確かめてから固定版を上げてください。

## Compiler APIを直接使うべきでない場面

### ts-morphで十分なケース

Compiler API を直接叩く判断は、多くのプロジェクトで過剰です。クラスにメソッドを足す、import 文を並べ替える、型定義からプロパティ一覧を取り出すといった作業は、ts-morph なら AST ノードを組み立てずにメソッド呼び出しで書けます。変更後のフォーマット維持も ts-morph が担い、TypeScript 本体を同梱しているのでバージョン整合の管理も1つ減ります。

Compiler API を直接使う価値があるのは、TypeChecker の細かな挙動に依存する型解析、コンパイル前に AST を差し替えるカスタムトランスフォーマ、tsc の出力そのものへの介入といった、ts-morph が抽象化しきれない領域です。要件がノードの読み書きに収まるなら ts-morph を選んでください。

### lintルールの実装にCompiler APIを選ぶ失敗

コーディング規約のチェックを自作したい、という動機で Compiler API に手を出すのは典型的な遠回りです。ファイル探索・キャッシュ・エラー報告の形式・エディタ統合・自動修正まで、すべて自前で書くことになります。typescript-eslint のカスタムルールならルール本体だけで済み、型情報が要るルールでも parserServices 経由で TypeChecker に届きます。

速度が問題なら、解析基盤ごと替える選択もあります。Rust 製の[Oxcとは？Rust製JavaScriptツールチェーンの構成と使い方](/tech/details/9737/)は TypeScript の AST 解析を JavaScript のランタイムの外で処理し、ESLint との違いは[OxlintとESLintの違いとは？読み方・速度実測・移行手順](/tech/details/8351/)で扱っています。トランスパイルだけなら[SWCとは？Rust製コンパイラの設定と採用判断](/tech/details/16019/)、型チェックを Rust で回す選択肢には[tsrsとは：Rust製TypeScript型チェッカーの使い方](/tech/details/19029/)があります。自作 lint に Compiler API を使うのは、既存のルール基盤では表現できない検査がはっきりある場合に限ってください。

## よくある質問

### TypeScript Compiler APIの公式ドキュメントはどこにありますか？

6系までの API は、GitHub の microsoft/TypeScript の Wiki にある Using the Compiler API が一次資料です。最小のコンパイラや型チェッカの使い方のサンプルが載っています。7.1 の新 API は、2026年11月時点では TypeScript 7.1 Beta の公式発表と、パッケージの dist 配下にある型定義ファイル（sync なら dist/api/sync/api.d.ts）が最も正確な資料です。

### ts.createProgramが「見つからない」というエラーが出るのはなぜですか？

入っている typescript が 7系である可能性が高いです。npm install typescript とだけ実行すると latest の 7.0.2 が入り、ルートの `"typescript"` からは version と versionMajorMinor しか取れません。従来の書き方を続けるなら `npm install --save-exact typescript@6.0.3` で入れ直してください。7系で書くなら、7.1.0-beta 以降の typescript/sync から API を読み込み、api.createProgram を呼びます。

### TypeScript 7でもCompiler APIは使えますか？

使えますが、入口が違います。7.0.2 では typescript/unstable/sync の試験版 API しかなく、7.1.0-beta で typescript/sync・typescript/async などの正式な入口に移りました。Program・TypeChecker・visitEachChild・factory といった概念と名前の多くは残っていますが、TypeChecker の取り方や factory の引数が 6系と違うため、6系のコードはそのままでは動きません。

### TypeScriptコンパイラのソースコードはどこで読めますか？

2026年11月時点の [microsoft/TypeScript](https://github.com/microsoft/TypeScript) の main ブランチは Go 製の 7系の実装に置き換わっており、src ディレクトリはありません。5系・6系のコードは v5.9.3 や v6.0.3 などのタグを選ぶと読め、パーサ（src/compiler/parser.ts）、バインダ、型チェッカ（src/compiler/checker.ts）の順に処理が流れます。7系の npm パッケージと JavaScript 向け API は main の packages/typescript 配下にあります。移植の開発に使われていた microsoft/typescript-go はアーカイブ済みです。

### 解析対象のファイルが増えてProgramの生成が遅いときはどうすればよいですか？

createProgram は依存をたどって型定義ファイルまで読み込むため、対象が増えるほど初期化に時間がかかります。6系で同じプロセスから繰り返し解析するなら、毎回 createProgram を呼ばず、ts.createIncrementalProgram や oldProgram を渡した再生成で差分だけを処理してください。7.1 の新 API では snapshot.update が同じ役割を持ちます。

## 関連記事

- [TypeScript 7（TS7）とは？Go製tscで約10倍速く｜7.0.2への移行とtsgo・7.1 betaの現状](/tech/details/10175/)
- [TypeScript 6.0とは｜5から6への移行手順とts5to6・7.0との違い【6.0.3対応】](/tech/details/11066/)
- [TSRXとは？JSXの後継を掲げるTypeScript拡張の構文とReact導入手順](/tech/details/12439/)
- [Oxcとは？Rust製JavaScriptツールチェーンの構成と使い方・SWCとの違い](/tech/details/9737/)
- [SWCとは？Rust製コンパイラの設定と採用判断を実装目線で解説](/tech/details/16019/)

---

出典: [TypeScript Compiler APIの使い方｜AST解析・型取得のコードとTypeScript 7.1の新API](<https://www.issoh.co.jp/tech/details/3351/>)（株式会社一創）
