different-variables-types について
異なる概念には別々の変数を使い、変数名の再利用やユニオン型による型の拡張を避けるよう勧める TypeScript スキルです。意図を明確にし、型と推論をシンプルに保ちます。
主な機能
- 01用途ごとに変数を分ける
- 02無関係な値のユニオン型を避ける
- 03可能な場合は const を使う
- 04意味に沿った変数名
- 05型の絞り込みと拡張の区別
活用シーン
- 01再代入による型エラーの解消
- 02型の異なる値の分離
- 03ユニオン型を使うコードの簡素化
- 04一時的な結果に再利用する変数の明確化
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill different-variables-typesインストール後、Claude Code で/different-variables-typesを入力して読み込まれたか確認します。
詳細
異なる型には異なる変数を使う
概要
異なる目的で変数を使い回さないでください。
JavaScript では、どの変数にも任意の値を代入できます。しかし、TypeScript では、変数の型は一般に固定されています。異なる概念には異なる変数を使うことで、型安全性とコードの明瞭さが向上します。
このスキルを使う場面
- 異なる目的で同じ変数名を使い回す場合
- 異なる型の値が変数に代入される場合
- 再代入に対応するためだけにユニオン型を使う場合
- 変数の使い回しが原因の型エラーをデバッグする場合
絶対に守るルール
One variable, one type, one purpose.
Different concepts deserve different names.
覚えておきましょう:
- 変数の値は変わっても、型は一般に変わりません
- ユニオン型は単純な型より扱いにくいものです
constはletより適しています- 異なる名前を使うことで、意図が伝わります
検出:使い回しによる型エラー
let productId = "12-34-56";
fetchProduct(productId); // Expects string
productId = 123456;
// ~~~~~~~ Type 'number' is not assignable to type 'string'
fetchProductBySerialNumber(productId);
// Expects number
TypeScript は、最初の代入から productId の型を string と推論します。
ユニオン型での「修正」(問題があります)
// Works, but creates problems
let productId: string | number = "12-34-56";
fetchProduct(productId);
productId = 123456; // OK
fetchProductBySerialNumber(productId); // OK
// But now everywhere you use productId, you need type guards:
if (typeof productId === 'string') {
// ...
}
ユニオン型は扱いにくく、コードの意図を分かりにくくします。
より良い解決策:異なる変数を使う
const productId = "12-34-56";
fetchProduct(productId);
const serial = 123456;
fetchProductBySerialNumber(serial);
メリット:
- 無関係な概念を切り分けられます
- より具体的な変数名を使えます
- 型推論が向上します(型注釈が不要になります)
- 型が単純になります(ユニオン型ではありません)
- 変数を
constにできます
const がより適している理由
// let: type can be reassigned
let x = 'hello';
x = 'goodbye'; // OK
x = 42; // Error: different type
// const: value cannot change
const y = 'hello';
y = 'goodbye'; // Error: cannot reassign const
// No type ambiguity ever
const を使うと、人にも TypeScript にも理解しやすいコードになります。
シャドーイング:別の問題
const productId = "12-34-56";
fetchProduct(productId);
{
const productId = 123456; // Different variable, same name
fetchProductBySerialNumber(productId);
}
異なるスコープにある別々の変数なので、これは動作します。しかし、人には分かりにくいため、代わりに異なる名前を使うことをお勧めします。
型の絞り込みと変数の使い回し
TypeScript では、型を絞り込む(より具体的にする)ことができます。
let x: string | number = getValue();
if (typeof x === 'string') {
x // Type: string (narrowed)
x.toUpperCase(); // OK
}
型を広げる(より具体性を下げる)ことはこれとは異なり、機能しません。
let x = 'hello'; // Type: string
x = 42; // Error: can't widen to string | number
実際の例
// Bad: reusing 'result' for different things
let result = await fetchUser(id);
processUser(result);
result = await fetchPosts(id); // Error: different type!
processPosts(result);
// Good: descriptive names for each
const user = await fetchUser(id);
processUser(user);
const posts = await fetchPosts(id);
processPosts(posts);
圧力への対処プロトコル
1. 「変数を使い回したい」
圧力:「一時的なものだから、再代入するだけです」
回答: 新しい変数を使いましょう。そのほうが明確で、型安全です。
対応: 説明的な名前を付けた新しい const を作成します。
2. 「ユニオン型で問題ない」
圧力:「string | number を使えばいいだけです」
回答: ユニオン型を使うと、その変数を使うあらゆる箇所が複雑になります。
対応: 単純な型を持つ別々の変数のほうが、扱いやすくなります。
危険信号 — 立ち止まって再検討しましょう
- A と B が異なる目的で使われるのに、
let x: A | Bとしている - まったく異なる値に再代入される変数
- 再代入による型エラー
tempやresultのような汎用的な変数名を使い回している
よくある正当化(すべて誤りです)
| 言い訳 | 実際 |
|---|---|
| 「変数を節約できます」 | 変数は安価ですが、バグは高くつきます |
| 「同じデータで、形式が違うだけです」 | 形式が違うなら、変数も分けましょう |
| 「コードが少なくなります」 | コードの少なさより、明瞭さが大切です |
クイックリファレンス
// DON'T: Reuse variable for different purposes
let id = "abc";
fetchByString(id);
id = 123; // Error
// DON'T: Use union just to allow reassignment
let id: string | number = "abc";
// DO: Different variables for different concepts
const stringId = "abc";
const numericId = 123;
要点
異なる概念には、それぞれ異なる変数を使いましょう。
TypeScript の型は(絞り込みを除いて)変わりません。目的ごとに別々の変数を使うことで、この性質を活用しましょう。コードが明確になり、型が単純になり、バグも減ります。
参考資料
Dan Vanderkam 著『Effective TypeScript』の項目 19「異なる型には異なる変数を使う」に基づいています。