prefer-type-annotations について
prefer-type-annotationsは、TypeScript開発者に型アサーションより型注釈を優先するよう促すスキルです。型注釈による値の検証を説明し、危険なアサーションの見分け方や限られた例外の扱いを示します。
主な機能
- 01値の型を検証する注釈を優先
- 02`as`アサーションを見直す
- 03関数の戻り値に型注釈を付与
- 04DOMなどアサーションが適切な場面を把握
- 05実行時チェックで型を絞り込む
活用シーン
- 01変数やオブジェクトリテラルの型を宣言
- 02関数の戻り値の型を定義
- 03型の不整合エラーを調査
- 04型アサーションが必要か判断
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill prefer-type-annotationsインストール後、Claude Code で/prefer-type-annotationsを入力して読み込まれたか確認します。
詳細
型アノテーションを型アサーションより優先する
概要
型アノテーション(: Type)は値が型に適合することを検証します。型アサーション(as Type)はTypeScriptに信頼するよう伝えます。
アノテーションはコードをチェックします。アサーションはチェックを回避します。選べる場合は、アノテーションを優先してください。
このスキルを使う場面
- 変数に値を代入するとき
- 関数の戻り値の型を定義するとき
- オブジェクトリテラルを扱うとき
- エラーを解消するために
as Typeを使いたくなったとき - 「代入できません」というエラーが発生したとき
鉄則
NEVER use type assertions when type annotations would work.
例外はありません:
- 「明らかに正しい」から
- 「TypeScriptより自分のほうが分かっている」から
- 「アサーションのほうが簡単」だから
検出:「as」の匂い
as SomeTypeを見かけたら、こう考えてください:「代わりに型アノテーションを使えないだろうか?」
// ❌ VIOLATION: Type assertion bypasses checking
const alice = {} as Person; // No error! But alice has no properties
// ✅ CORRECT: Type annotation verifies the value
const alice: Person = {};
// ~~~~~ Property 'name' is missing in type '{}' but required in type 'Person'
重要な違い
型アノテーション(安全)
interface Person { name: string }
// TypeScript CHECKS that the value matches the type
const bob: Person = { name: 'Bob' }; // OK
const bad: Person = {}; // Error: missing 'name'
const extra: Person = {
name: 'Carol',
age: 30 // Error: 'age' does not exist on type 'Person'
};
型アサーション(安全ではない)
// TypeScript TRUSTS you that the value matches the type
const bob = {} as Person; // No error, but wrong!
const bad = { foo: 1 } as Person; // No error, but wrong!
アロー関数:戻り値にアノテーションを付ける
// ❌ VIOLATION: Assertion hides errors
const people = ['alice', 'bob'].map(name => ({name} as Person));
// Even worse: completely wrong values slip through
const people = ['alice', 'bob'].map(name => ({} as Person)); // No error!
// ✅ CORRECT: Annotate the return type
const people = ['alice', 'bob'].map((name): Person => ({name})); // OK
const people = ['alice', 'bob'].map((name): Person => ({}));
// ~~ Error: missing 'name'
アサーションが適切な場合
型アサーションが適切なのは、TypeScriptよりも確かな情報を実際に持っている場合です。
1. DOM 要素
// You know #myButton exists and is a button
const button = document.querySelector('#myButton') as HTMLButtonElement;
// Better: include a comment explaining why
const button = document.querySelector('#myButton') as HTMLButtonElement;
// This button is created in index.html and always exists
2. 実行時チェックの後
const el = document.getElementById('foo');
if (el) {
el.innerHTML = 'Hello'; // TypeScript knows el is not null
}
// Or with non-null assertion (use sparingly!)
const el = document.getElementById('foo')!;
圧力に負けないための手順
1. 「エラーが間違っている」
圧力:「TypeScriptは文句を言っていますが、コードは正しいです」
**対応:**通常、TypeScriptの判断は正しいです。エラーメッセージを注意深く読んでください。
行動:
- 型定義が意図と一致しているか確認する
- 値が実際にその型に一致しているか確認する
- TypeScriptが間違っている理由を説明できる場合に限り、アサーションを使う
2. 「アサーションのほうが簡単」
圧力:「あちこちにアノテーションを追加するのは冗長です」
**対応:**アノテーションはバグを検出します。アサーションはバグを隠します。安全性 > 簡潔さです。
**行動:**アノテーションを追加してください。未来の自分に感謝されるでしょう。
3. 「実行時には型が分かっている」
圧力:「実行時に型をチェックしたので、アサーションは安全です」
**対応:**実行時にチェックしたのであれば、TypeScriptで型を絞り込めるはずです。
**行動:**アサーションではなく、型ガードや条件式を使って型を絞り込んでください。
注意信号 — 立ち止まって再検討する
- コードのどこかに
as anyがある - オブジェクトの作成直後に
as Typeを使っている - アサーションを連鎖している(
x as A as B) - 「代入できません」というエラーの解消にアサーションを使っている
- 十分な理由なく
!(非 null アサーション)を使っている
よくある正当化(すべて不適切)
| 言い訳 | 実態 |
|---|---|
| 「これはPersonだと分かっている」 | ならば、アサーションではなくアノテーションで証明してください。 |
| 「TypeScriptが間違っている」 | TypeScriptは実際の不整合を検出しました。原因を調べてください。 |
| 「アサーションは1つだけ」 | アサーションは広がります。1つが多くのアサーションにつながります。 |
| 「型が厳しすぎる」 | 厳密な型はバグを検出します。受け入れましょう。 |
クイックリファレンス
| 状況 | アノテーションを使う | アサーションを使う |
|---|---|---|
| 変数宣言 | :Type | 使わない |
| 関数の引数 | :Type | 使わない |
| 関数の戻り値 | :Type | まれに使う |
| オブジェクトリテラル | :Type | 使わない |
| 型が分かっている DOM 要素 | - | コメントを添えてas HTMLElement |
| null チェックの後 | - | 確実な場合は! |
| API からの不明な値 | - | unknownで検証した後 |
非 null アサーション(!)
// Tells TypeScript: "Trust me, this isn't null"
const el = document.getElementById('foo')!;
// Prefer a runtime check:
const el = document.getElementById('foo');
if (!el) throw new Error('No element #foo');
// Now TypeScript knows el is not null
要点
型アノテーションは契約です。型アサーションはTypeScriptにつく嘘です。
意図を宣言するにはアノテーションを使ってください。アサーションは、TypeScriptが持ち得ない情報を自分が持っている場合に限って使ってください。アサーションが有効である理由を説明するコメントを必ず添えてください。
参考資料
Dan Vanderkam著『Effective TypeScript』の項目9「型アサーションより型アノテーションを優先する」に基づいています。