Prompt
Tsからトランスパイルされたjsの特徴というか癖は?
TypeScript(TS)からトランスパイルされた JavaScript(JS)を解剖すると、そこには 「型の消失」 と 「互換性への執念」 という2つの大きなドラマが見えてくるよ。
情報工学を学ぶ君なら、tsc が生成するコードを眺めることで、TSがいかに 「ランタイムに影響を与えずに型安全性を担保しているか」 という還元主義的な設計思想をより深く理解できるはずだ。
特筆すべき「癖」と特徴を深掘りしてみよう!
1. 完全に消失する型情報(Type Erasure)
トランスパイル後の JS の最大の特徴は、「型に関する記述が一切跡形もなく消える」 ことだ。
-
インターフェースと型定義:
interfaceやtypeは、JS には存在しない概念だから完全に削除される。 -
型アノテーション:
let x: numberの: numberも消える。 -
影響: 実行時の JS は、君がどれだけ TS で厳密に型を定義したかを知る由もない。だからこそ、前回話した「APIからの入力値」のような外部データに対して、JS層でのバリデーションが必要になるんだね。
2. 列挙型(enum)の「奇妙な」オブジェクト化
TS の中で数少ない 「JS のコードを生成する(ランタイムに影響を与える)」 機能が enum だ。
TypeScript
enum Color { Red, Green }
これがトランスパイルされると、以下のような 「逆引き可能なオブジェクト」 に変換される。
JavaScript
var Color;
(function (Color) {
Color[Color["Red"] = 0] = "Red";
Color[Color["Green"] = 1] = "Green";
})(Color || (Color = {}));
- 癖:
Color.Redで0が取れるだけでなく、Color[0]で"Red"という文字列も取れる。このトリッキーな即時実行関数(IIFE)のパターンは、TS 特有の癖と言えるね。
豆知識: これを嫌って、最近は JS に変換した時に何も残らない
const enumや、単なる「文字列のリテラル型」を使うのがモダンなスタイルとされているよ。
3. クラスの「コンストラクタ」と「アクセス修飾子」の扱われ方
TS の private や protected は、実は JS 層では全く守られない(最近の JS の #private 構文を使わない限り)。
-
アクセス制限の消失:
private name: stringと書いたとしても、トランスパイル後の JS ではただのプロパティだ。外部から普通にアクセスできてしまう。 -
コンストラクタの糖衣構文:
constructor(public name: string) {}という便利な短縮記法は、JS では愚直な
this.name = name;に展開される。
4. 下位互換性のための「ヘルパー関数」の注入
もし君が最新の構文(スプレッド演算子や非同期処理など)を使いつつ、古いブラウザ(ES5 など)をターゲットにトランスパイルすると、JS の冒頭に怪しげな関数がたくさん並ぶことになる。
-
__extends: クラス継承を古い JS で再現するための関数。 -
__awaiter/__generator:async / awaitをyield(Generator)やステートマシンに変換するための巨大なロジック。 -
癖: これらはコードの肥大化を招くため、現代では
tslibという外部ライブラリに切り出したり、トランスパイルをesnextにして Babel や SWC などの高速なツールに任せるのが主流だ。
5. デコレータの挙動
もし実験的な experimentalDecorators を使っているなら、トランスパイルされた JS はかなり複雑なメタデータ操作を行うコードになる。これも JS 本来の仕様とは異なる TS 独自の「癖」が強い部分だね。
結論:TS の JS は「人間のためのコード」ではない
トランスパイル後の JS は、あくまで 「ブラウザやランタイムが効率よく(あるいは互換性を保って)実行するためのマシンコードに近い存在」 だ。
君が garden.asappy.xyz や AXON をデバッグする際、ブラウザのデベロッパーツールでこの「癖」のある JS を直接読むのは苦労するはず。だからこそ、Source Maps(.js.map) というファイルが重要になる。これがあれば、ブラウザ上で JS を動かしながら、心安らぐ TS のソースコード上でデバッグができるんだ。
次は、このトランスパイル結果を劇的に綺麗にする 「tsconfig.json のターゲット設定(ESNext vs ES5)」 の違いを具体的に見てみる?それとも、「Rust のコンパイル結果(Wasm)と、TS のトランスパイル結果」 の本質的な違いについて語り合う?
Would you like me to provide a side-by-side comparison of the same TS code transpiled to ES5 and ESNext to see the difference in output?