code-gen-independent について
型チェックと JavaScript 生成が別の処理であり、型が実行時には消去されることを解説する TypeScript スキルです。開発者が型宣言に頼らず、実際の実行時チェックや変換を選ぶために役立ちます。
主な機能
- 01型チェックとコード生成の区別
- 02実行時の型消去の説明
- 03プロパティやタグによる検査例
- 04型アサーションと変換の違い
- 05型によるオーバーロードの問題
活用シーン
- 01型エラーがあっても JavaScript が生成される理由の調査
- 02インターフェースの実行時検査方法の選択
- 03型アサーションに頼らない値の変換
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill code-gen-independentインストール後、Claude Code で/code-gen-independentを入力して読み込まれたか確認します。
詳細
コード生成は型とは独立していることを理解する
概要
TypeScript のコンパイルと型チェックは別々の処理です。
TypeScript コンパイラーは、(1)TypeScript を JavaScript にトランスパイルすることと、(2)型をチェックすることの 2 つを行います。これらは独立しており、型エラーがあってもコード生成は妨げられず、型は実行時には存在しません。
このスキルを使う場面
- 型エラーがあるのにコードが実行される理由が分からない
- 実行時に型をチェックしようとしている
- インターフェースに
instanceofを使っている - 型が実行時の動作に影響すると期待している
絶対に守るべきルール
NEVER expect TypeScript types to exist at runtime. They are erased.
重要な事実:
- 型エラーがあるコードでも、JavaScript の出力を生成できます
- 型はコンパイル時に消去されます
- TypeScript のインターフェースに
instanceofは使えません - 実行時のチェックには JavaScript の構文が必要です
型エラーが出力を妨げることはありません
$ cat test.ts
let x = 'hello';
x = 1234; // Type error!
$ tsc test.ts
test.ts:2:1 - error TS2322: Type 'number' is not assignable to type 'string'
$ cat test.js
var x = 'hello';
x = 1234; // JavaScript was still generated!
型エラーは警告のようなもので、コンパイルを停止させません。
実行時に型をチェックすることはできません
interface Square {
width: number;
}
interface Rectangle extends Square {
height: number;
}
type Shape = Square | Rectangle;
function calculateArea(shape: Shape) {
if (shape instanceof Rectangle) {
// ~~~~~~~~~ 'Rectangle' only refers to a type,
// but is being used as a value here
return shape.width * shape.height;
}
return shape.width * shape.width;
}
なぜでしょうか? interface と type は消去されます。JavaScript には存在しません。
実行時の型チェック方法
方法 1:プロパティのチェック
function calculateArea(shape: Shape) {
if ('height' in shape) {
// TypeScript knows shape is Rectangle here
return shape.width * shape.height;
}
return shape.width * shape.width;
}
方法 2:タグ付きユニオン(推奨)
interface Square {
kind: 'square';
width: number;
}
interface Rectangle {
kind: 'rectangle';
width: number;
height: number;
}
type Shape = Square | Rectangle;
function calculateArea(shape: Shape) {
if (shape.kind === 'rectangle') {
return shape.width * shape.height;
}
return shape.width * shape.width;
}
方法 3:クラスを使う
class Square {
constructor(public width: number) {}
}
class Rectangle extends Square {
constructor(width: number, public height: number) {
super(width);
}
}
function calculateArea(shape: Square | Rectangle) {
if (shape instanceof Rectangle) {
// This works! Classes exist at runtime
return shape.width * shape.height;
}
return shape.width * shape.width;
}
型操作は実行時に影響しません
function asNumber(val: number | string): number {
return val as number; // Type assertion
}
// Generated JavaScript:
function asNumber(val) {
return val; // No conversion! Just returns val as-is
}
型アサーションは値を変換しません。実行時のコードを使ってください。
function asNumber(val: number | string): number {
return typeof val === 'string' ? Number(val) : val;
}
実行時の型は宣言された型と異なる場合があります
function setLightSwitch(value: boolean) {
switch (value) {
case true: turnLightOn(); break;
case false: turnLightOff(); break;
default:
console.log("I'm afraid I can't do that.");
// TypeScript thinks this is unreachable, but...
}
}
// At runtime, someone could call:
setLightSwitch("ON" as any); // Hits the default case!
API レスポンス、外部データ、any 型により、実行時の型の不一致が起こることがあります。
型に基づくオーバーロードはできません
// ❌ This doesn't work - types are erased
function add(a: number, b: number) { return a + b; }
function add(a: string, b: string) { return a + b; }
// ~~~ Duplicate function implementation
// ✅ Use a single implementation with union type
function add(a: number | string, b: number | string) {
if (typeof a === 'number' && typeof b === 'number') {
return a + b;
}
return String(a) + String(b);
}
圧力に負けないためのプロトコル
1. 「as Type を使えばいいだけ」
圧力:「型アサーションで値を変換できます」
**回答:**型アサーションは値を変換しません。コンパイル時にのみ有効です。
**対応:**実際に実行時に変換するコードを書いてください。
2. 「instanceof を使う」
圧力:「instanceof は自分のインターフェースでも動くはずです」
**回答:**インターフェースは実行時には存在しません。タグ付きユニオンかクラスを使ってください。
**対応:**判別用プロパティを追加するか、クラスを使ってください。
危険な兆候 ― 立ち止まって再考してください
- インターフェースや型に
instanceofを使う - 型アサーションで値が変換されると期待する
- 型エラーが JavaScript の出力を妨げると思い込む
- 実行時の検証を TypeScript の型に頼る
クイックリファレンス
| TypeScript の構文 | 実行時に存在するか |
|---|---|
interface | いいえ |
type | いいえ |
型注釈(: T) | いいえ |
型アサーション(as T) | いいえ |
class | はい |
enum(const なし) | はい |
| タグ付きユニオンのプロパティ | はい |
要点
型は消去されます。コード生成は型チェックとは独立しています。
TypeScript の型が存在するのはコンパイル時だけです。実行時の型チェックには、プロパティのチェック、タグ付きユニオン、クラス、または typeof/instanceof(型ではなく値に対して)といった JavaScript の構文を使ってください。
参考資料
Dan Vanderkam 著『Effective TypeScript』項目 3「コード生成は型とは独立していることを理解する」に基づいています。