【TypeScript】コンパイラAPIを使用した型の解析 - 型情報を取得・操作する方法

【TypeScript】コンパイラAPIを使用した型の解析 - 型情報を取得・操作する方法

2024-10-25

2024-10-25

TypeScriptコンパイラAPIは、TypeScriptのコンパイラ機能にプログラムからアクセスし、コードの型情報を解析したり操作したりするための強力なツールです。これを使うことで、コード解析ツールや静的型チェックツール、コードトランスパイラなどを自作できるようになります。本記事では、TypeScriptのコンパイラAPIを活用して、型情報の取得やAST(抽象構文木)の解析を行う方法を解説します。

コンパイラAPIとは?

TypeScriptコンパイラAPIは、TypeScriptコンパイラ(tsc)の内部機能にアクセスできるAPIセットです。これにより、TypeScriptコードの構文解析や型チェック、AST(抽象構文木)生成などの操作が可能になります。 具体的には、コンパイラAPIを使用することで次のような処理ができます。

  • 型情報の取得:ソースコード内の変数や関数の型情報を取得できます。
  • ASTの解析:コードをAST(抽象構文木)に変換し、その構造を解析・操作できます。
  • コード変換:ASTを操作して、コードを別の形に変換したり、トランスパイルできます。
  • カスタム静的解析ツールの作成:独自の型チェックルールを追加する静的解析ツールを作成できます。

コンパイラAPIの基本的な使い方

まずは、コンパイラAPIを利用するための環境を整えましょう。TypeScriptコンパイラAPIはTypeScript自体に含まれているため、新たにインストールする必要はありません。typescriptパッケージをインストールしていれば、すぐに利用できます。

npm install typescript --save-dev

ASTの生成と解析

コンパイラAPIの中心的な機能の一つは、TypeScriptコードをAST(抽象構文木)に変換し、コードの構造を解析することです。以下は、コードをASTに変換する簡単な例です。

import * as ts from 'typescript';
// ソースコードの文字列
const code = `
  function greet(name: string): string {
    return 'Hello, ' + name;
  }
`;
// ソースコードからASTを生成
const sourceFile = ts.createSourceFile(
  'example.ts',
  code,
  ts.ScriptTarget.Latest,
  true
);
// ASTをトラバースして解析する関数
function visit(node: ts.Node) {
  // ノードの種類を表示
  console.log(ts.SyntaxKind[node.kind]);
  // 子ノードを再帰的に訪問
  node.forEachChild(visit);
}
// トップレベルのノードからトラバース開始
visit(sourceFile);

このコードでは、ts.createSourceFileを使用して、文字列で表現されたTypeScriptコードをASTに変換しています。そして、visit関数を使ってASTの各ノードを再帰的に訪問し、ノードの種類(構文の種類)を表示しています。

型情報の取得

TypeScriptのコンパイラAPIを使うことで、コード内の変数や関数の型情報を取得することができます。次に、型情報を取得する例を紹介します。

import * as ts from 'typescript';
// ソースコードの文字列
const code = `
  const age: number = 30;
  const name: string = 'Alice';
`;
// `TypeScript`プログラムを生成
const sourceFile = ts.createSourceFile(
  'example.ts',
  code,
  ts.ScriptTarget.Latest,
  true
);
// プログラム全体を作成
const program = ts.createProgram(['example.ts'], { noEmit: true });
const checker = program.getTypeChecker();
// ASTをトラバースして型情報を取得する関数
function visit(node: ts.Node) {
  if (ts.isVariableDeclaration(node) && node.name) {
    const symbol = checker.getSymbolAtLocation(node.name);
    if (symbol) {
      const type = checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration!);
      console.log(`${symbol.getName()}: ${checker.typeToString(type)}`);
    }
  }
  node.forEachChild(visit);
}
// トップレベルのノードからトラバース開始
visit(sourceFile);

この例では、TypeScriptの型チェッカー(getTypeChecker)を使用して、変数の型情報を取得しています。typeToStringメソッドを使うことで、型情報を文字列に変換し、可視化しています。例えば、上記のコードを実行すると、age: numbername: stringのような結果が得られます。

型解析を活用した実際のユースケース

コンパイラAPIを使った型解析は、以下のような場面で非常に有効です。

カスタムリントツールの作成

TypeScriptの標準的なリントツールではサポートされていない特定のコーディング規則を追加したい場合、コンパイラAPIを使って独自のルールを作成できます。例えば、関数名に特定のプレフィックスが必要な場合や、特定の型の使用を制限するルールなどが考えられます。

コード自動生成ツール

コンパイラAPIを使用すると、ASTを操作して、コードを自動生成するツールを作成できます。例えば、TypeScriptの型定義からAPIクライアントを自動生成するツールや、クラスからデータベースのクエリを生成するツールがその一例です。

プロジェクトの型安全性の向上

大規模なプロジェクトで型の整合性をチェックするために、コンパイラAPIを活用してプロジェクト全体の型解析を行い、型のミスマッチや不完全な型定義を発見できます。特に型安全性が重要なプロジェクトで有効です。

ASTを操作する実際の例

ここでは、TypeScriptのコードをASTを通じて操作し、既存のコードに新しい要素を追加する例を示します。例えば、関数に新しい引数を追加する操作です。

import * as ts from 'typescript';
// ソースコード
const code = `
  function greet(name: string) {
    return 'Hello, ' + name;
  }
`;
// ASTの生成
const sourceFile = ts.createSourceFile(
  'example.ts',
  code,
  ts.ScriptTarget.Latest,
  true
);
// ノードを変更するためのトランスフォーム関数
function transformer(context: ts.TransformationContext) {
  return (rootNode: ts.SourceFile) => {
    function visit(node
: ts.Node): ts.Node {
      if (ts.isFunctionDeclaration(node)) {
        // 関数に新しい引数を追加
        const newParam = ts.createParameter(
          undefined, undefined, undefined,
          ts.createIdentifier('age'),
          undefined,
          ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)
        );
        const updatedParams = ts.createNodeArray([...node.parameters, newParam]);
        return ts.updateFunctionDeclaration(
          node,
          node.decorators,
          node.modifiers,
          node.asteriskToken,
          node.name,
          node.typeParameters,
          updatedParams,
          node.type,
          node.body
        );
      }
      return ts.visitEachChild(node, visit, context);
    }
    return ts.visitNode(rootNode, visit);
  };
}
// トランスパイル(ASTの変更を適用)
const result = ts.transform(sourceFile, [transformer]);
// 新しいコードを生成
const printer = ts.createPrinter();
const transformedCode = printer.printFile(result.transformed[0]);
console.log(transformedCode);

この例では、greet関数に新しい引数ageを追加し、その結果を出力しています。コンパイラAPIを使えば、TypeScriptコードを自由に操作して、必要な変更を自動的に加えることができます。

まとめ

TypeScriptのコンパイラAPIは、コード解析や型情報の取得、ASTの操作を通じて、強力なツールを構築するための基盤を提供します。静的解析ツールの作成、型情報に基づくカスタム処理、自動コード生成など、さまざまな用途に活用できるため、特に大規模なプロジェクトでの型安全性の確保や開発効率の向上に貢献します。コンパイラAPIをうまく活用して、より高度なTypeScript開発を進めてみましょう。

Recommend