この記事は CyberAgent Developers Advent Calendar 2025 4日目の記事です。

はじめに

グループIT推進本部 CyberAgent group Infrastructure Unit(以下、CIU)所属・Next Expertsの平井( @did0es )です。

CIUのサービスのWebフロントエンドの開発に携わる傍ら、TypeScriptのNext Expertsとして情報発信や社内向けの技術支援を中心に活動しています。

本記事では、TypeScript Compiler(以下、tsc)の compilerOptions のうち、コードスタイルに関わるものをおさらいしつつ、JavaScript・TypeScriptによる開発で広く用いられているESLintと機能や構造を比較し、tscをコンパイラとしてだけでなくLinterとしても活用できるか考察します。

tsc = Linter?

早速ですが、tscはLinterと見なせるでしょうか?

tscはコンパイラとしてTSの型情報をふるい落とし、実行可能なJSに変換する役割を担っています。変換の際、静的解析によって不正なものは型エラーとして報告されます。

これはLinterの検査・結果のレポートと非常に似た挙動です。

また、tscの設定ファイルの tsconfig.json に記述する compilerOptions には、コードスタイルに関するプロパティが存在しています。

追加された際のTypeScriptのメジャーバージョンと併せて紹介します。

  • TypeScript 1系以降
    • noImplicitReturns:関数内でreturnを書かなかった場合エラーにする
    • noFallthroughCasesInSwitch:switch文でreturnやbreakがブロック内にない場合エラーにする
    • allowUnreachableCode:到達不可能なコードに関するエラーを無効にする(デフォルトは有効)
    • allowUnusedLabels:未使用のラベルがあった際のエラーを無効にする(デフォルトは有効)
    • forceConsistentCasingInFileNames :import時のパスと実際のファイル名の大文字小文字を区別し、一致しない場合はエラーにする
    • noImplicitAny :暗黙の any はエラーにする
  • TypeScript 2系以降
  • TypeScript 4系以降
    • noPropertyAccessFromIndexSignature : 型にインデックスシグネチャがあるオブジェクトについて、インデックスアクセス( obj[”key”] )ではなくドットによるアクセス( obj.key)を行った場合エラーにする
    • exactOptionalPropertyTypes :省略可能な値に undefined をセットするとエラーにする(省略可能と undefined を区別する)
    • noUncheckedIndexedAccess :インデックスアクセスした値の型に | undefined をつける
    • noImplicitOverride :親クラスのメソッドをoverrideするときに、 overide キーワードを付けないとエラーにする

こういったオプションから、tscはただTypeScriptをJavaScriptに変換するだけでなく、既存のコードスタイルに対して関心を持っている点から、Linterと見なせるかもしれません。

 

併せて、以下のような erasableSyntaxOnlyオプションを有効化したときに使える、JavaScriptには反映されないTypeScriptの構文も、コードの健全性を担保している点でLinterの一部として見なせます。

  • 型注釈: const foo: number のような記述。型アノテーション
  • 型エイリアス: type Foo = {} のような記述
  • インターフェース: interface Foo {} のような記述
  • ジェネリクス: function foo<T>() {} のような記述。型引数
  • 型アサーション: foo as Bar のような記述。型のキャスト
  • 非nullアサーション: foo! のような記述
  • 型ユーティリティ:Mapped TypesConditional Typesなどの型
  • アンビエント宣言: declare 〇〇 のような記述
  • Type Only import/export: import type 〇〇export type 〇〇 のような記述

 

次の項では、構造の観点からtscとLinter(ESLint)を比較します。

tscとLinterの構造の比較

以下はtscとESLintの処理フローです。

tscとeslintの処理の流れをそれぞれ表した2つのフローチャート。上にtscで下にeslintのフローが示されている。tscはソースコードとtsconfig.jsonを受け取り、トランスパイルしたJSと検査結果(エラー)を出力する。eslintはソースコードとeslint.config.jsを受け取り、検査結果(エラー)とfixする場合は修正されたソースコードを出力する。

tscは実行されるとコンフィグファイルの tsconfig.json を読み込み、 compilerOptions などの内容に基づいてコンパイルします。

ESLintも同じように、実行されると eslint.config.js のようなコンフィグファイルを読み込み、 rules などの内容に基づいてLintします。

どちらもユーザーが選択したオプションを元に処理を実行します。

 

また、tscもESLintもAbstract Syntax Tree(以下、AST)による静的解析に基づいたツールです。

静的解析とは、コードを直接実行することなくどういった挙動をするのかを解析する技術です。

解析にはASTが用いられます。ASTとは、ソースコードを木構造にして、効率よく探索できるようにした中間表現です。

JavaScriptやTypeScriptの世界ではASTをオブジェクトとして取り回します。

このASTはツールによって異なります。標準化こそされていませんがAcornEsprimaなどの形式や、こういった既存の形式をフォークして改変したものが広く用いられています。

 

tscはソースコードを独自のTypeScript ASTに変換し、再帰的に探索しています。

この処理は、 TypeScript Compiler Notes というGitHubリポジトリで解説されています。適宜ご覧ください。

ESLintはソースコードをEspreeという形式のASTに変換し、再帰的かつイベント駆動型の仕組みで探索しています。

この処理は、自著の ESLintとPrettierのコードリーディングでASTベースの静的解析を理解する で解説しています。こちらも適宜ご覧ください。

どちらもASTの形式や探索方法は異なりますが、ソースコードを中間表現に変換し、再帰的に探索した結果を出力する点は同じです。

一方、tscとESLintの間には入出力に関して、明確な挙動の違いがあります。

tscはオプションと入力されたソースコードを照らし合わせてエラーをレポートしますが、エラーの修正は行わず、 CLIのオプションに沿って変換したJSを出力します。

ESLintはルールに違反した入力があればエラーとしてレポートしつつ、 fix オプションを付けるとルールに沿って修正したソースコードを出力します。

 

ESLintと比較してtscができること、できないことをまとめます。

tsc ESLint
コードの検査 △(ESLintほどではないが、tsc = Linter?の項で挙げたオプションや構文でできる
コードの修正
エディタ上でのエラー可視化
プラグインの実装

まとめ

機能や構造の観点からtscとLinterを比較しました。tscは機能的・構造的にかなりLinterに近いツールです。

しかし、Linterとしての役割を単体で賄えるものではないため、ESLintのようなツールと併用する場面が多く見受けられます。

また、最近のtscの開発動向として、7系以降はGoによる再実装が行われており、パフォーマンス方面を注力するように見受けられます。

Linterとしての機能をより充実させるような方向性ではありませんが、開発体験の向上に向けた開発が日々行われています。

 

この記事を通して、tscやTypeScriptを取り巻くエコシステムに関心を持っていただければ幸いです。

ご精読いただきありがとうございました。

アバター画像
22年新卒入社 グループIT推進本部 CIU所属 ソフトウェアエンジニア・Next Experts(TypeScript)|Lead organizer for FrontEnd Conference Tokyo, Meguro.es, organizer for FrontEnd Conference Nagoya