consistent-aliases について
TypeScript の制御フローによる型の絞り込みを意図どおりにするため、エイリアスを一貫して扱う方法を解説するガイドです。エイリアスと元のプロパティが別々に追跡される点を踏まえ、分割代入やローカル変数、readonly パラメーターを勧めます。
主な機能
- 01エイリアスか元のプロパティに統一
- 02分割代入で名前をそろえる
- 03関数呼び出しで値が変わる場合はローカル変数を使用
- 04readonly パラメーターで型の絞り込みを保護
- 05Map.get やフォールバックの安全な書き方
活用シーン
- 01予期しない TypeScript の型絞り込みエラーの解決
- 02プロパティ検査後の undefined の回避
- 03エイリアスとプロパティを混用したコードの確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill consistent-aliasesインストール後、Claude Code で/consistent-aliasesを入力して読み込まれたか確認します。
詳細
エイリアスは一貫して使いましょう
概要
エイリアスを作成したら、一貫して使いましょう。
オブジェクトのプロパティを変数に代入すると、エイリアスが作成されます。TypeScript はこれらを別々に追跡するため、一方を絞り込んでも、もう一方は絞り込まれません。どちらか一方を選び、使い続けましょう。
このスキルを使う場面
- 型の絞り込みが想定どおりに機能しない
- 変数とその参照元のプロパティを両方使っている
- プロパティのチェックがエイリアス変数に反映されない
- 関数呼び出しの後に型の絞り込みが「失われる」
絶対に守るルール
One alias OR the original - never mix them in the same scope.
Prefer destructuring for consistent naming.
覚えておきましょう:
- エイリアスと元の値は別々に追跡される
- 一方を絞り込んでも、もう一方は絞り込まれない
- 関数呼び出しによって、プロパティの型の絞り込みが無効になることがある
- オブジェクトのプロパティよりローカル変数のほうが安全
検出:エイリアスによって型の絞り込みが機能しない
interface Polygon {
exterior: Coordinate[];
bbox?: BoundingBox;
}
function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
const box = polygon.bbox; // Created an alias
if (polygon.bbox) { // Narrows polygon.bbox
if (pt.x < box.x[0]) { // box is still possibly undefined!
// ~~~
// 'box' is possibly 'undefined'
}
}
}
polygon.bbox に対するチェックでは、box の型は絞り込まれません。
基本ルール:エイリアスを一貫して使う
function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
const box = polygon.bbox;
if (box) { // Check the alias
if (pt.x < box.x[0]) { // Use the alias
// OK - box is narrowed here
}
}
}
これで、チェックと使用箇所の両方が同じ変数を参照します。
ベストプラクティス:分割代入を使う
function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
const { bbox } = polygon; // Same name as property
if (bbox) {
const { x, y } = bbox; // Continue destructuring
if (pt.x < x[0] || pt.x > x[1] || pt.y < y[0] || pt.y > y[1]) {
return false;
}
}
return true;
}
利点:
- 名前に一貫性がある(
boxとbboxの混同を防げます) - より簡潔です
- TypeScript の制御フローとうまく連携します
実行時にもエイリアスの影響があります
エイリアスは実行時の動作にも影響します。
const { bbox } = polygon;
if (!bbox) {
calculatePolygonBbox(polygon); // Fills in polygon.bbox
// Now polygon.bbox exists, but bbox is still undefined!
}
実行時には、エイリアスと元の値が異なることがあります。
関数呼び出しと型の絞り込み
TypeScript は、関数呼び出しについて実用的な判断をします。
function expandPolygon(p: Polygon) { /* ... */ }
if (polygon.bbox) {
polygon.bbox // BoundingBox (narrowed)
expandPolygon(polygon);
polygon.bbox // Still BoundingBox (TypeScript trusts you)
// But the function might have set it to undefined!
}
TypeScript は、関数が型の絞り込みを無効にしないと想定します。通常は問題ありませんが、誤りとなる場合もあります。
より安全な方法:ローカル変数
if (polygon.bbox) {
const bbox = polygon.bbox; // Capture it locally
expandPolygon(polygon);
bbox // Still BoundingBox - local variable is safe
}
ローカル変数は関数呼び出しによって変更されません。
安全性をさらに高めるために readonly を使う
function safeExpand(p: Readonly<Polygon>) {
// Can't modify p.bbox
}
if (polygon.bbox) {
safeExpand(polygon);
polygon.bbox // Guaranteed still BoundingBox
}
readonly パラメーターを使えば、変更に関する懸念を防げます。
よくあるパターン
エイリアスの代わりに Null 合体演算子を使う
// Instead of:
const name = person.nickname;
const displayName = name ? name : person.fullName;
// Use:
const displayName = person.nickname ?? person.fullName;
Map.get パターン
// TypeScript doesn't connect has() and get():
if (map.has(key)) {
const value = map.get(key); // Still T | undefined
}
// Better pattern:
const value = map.get(key);
if (value !== undefined) {
value // T (narrowed)
}
// Or with nullish coalescing:
const value = map.get(key) ?? defaultValue;
プレッシャーに対する対処法
1. 「両方の名前が必要です」
プレッシャー:「プロパティを使うこともあれば、変数を使うこともあります」
回答: どちらか一方を選びましょう。混在させると、型の絞り込みが機能しなくなります。
対処: 一貫した名前を使えるよう、分割代入を使いましょう。
2. 「関数は変更しません」
プレッシャー:「expandPolygon が bbox に触れないことはわかっています」
回答: TypeScript にはそれがわかりません。Readonly を使って、そのことを明示しましょう。
対処: 安全性を高めるため、ローカル変数か readonly パラメーターを使いましょう。
注意すべき兆候 — 作業を止めて再検討しましょう
- プロパティから変数を作成した後、条件式でそのプロパティを使っている
ifチェックの後に「未定義の可能性がある」という型エラーが発生する- 同じブロック内でプロパティへのアクセスと変数の使用が混在している
- 関数呼び出し後も型の絞り込みが維持されると想定している
よくある誤った正当化
| 言い訳 | 実際 |
|---|---|
| 「エイリアスは同じものです」 | TypeScript は別々に追跡します |
| 「プロパティはすでにチェックしました」 | チェックしても、エイリアスの型は絞り込まれません |
| 「関数は変更しません」 | TypeScript はそれを確認できません |
クイックリファレンス
// DON'T: Mix alias and original
const box = polygon.bbox;
if (polygon.bbox) { // Checks original
box.x; // Uses alias - still possibly undefined!
}
// DO: Use alias consistently
const box = polygon.bbox;
if (box) { // Checks alias
box.x; // Uses alias - narrowed correctly
}
// DO: Use destructuring
const { bbox } = polygon;
if (bbox) {
const { x, y } = bbox;
}
要点
名前を一つ選び、一貫して使いましょう。
プロパティにエイリアスを付けると、TypeScript はそのエイリアスと元の値を別々に追跡します。一方を絞り込んでも、もう一方は絞り込まれません。一貫した名前を使うには分割代入を使い、関数呼び出しによって型の絞り込みが無効になる可能性がある場合は、ローカル変数を優先しましょう。
参考資料
Dan Vanderkam 著『Effective TypeScript』の項目 23「エイリアスは一貫して使いましょう」に基づいています。