imprecise-over-inaccurate について
型を狭くすることより、実態に合った正確さを優先する TypeScript のスキルです。有効なコードを拒否する型を避け、実行時検証を使う場面を判断できます。
主な機能
- 01狭さより正確さを優先
- 02誤検知の型エラーを把握
- 03複雑な領域には広い型
- 04型を段階的に精緻化
- 05不確かな値を実行時に検証
活用シーン
- 01精密な型が有効な入力を拒否
- 02複雑なジェネリック型のデバッグ
- 03CSS や外部データの型付け
- 04柔軟な JSON Schema 型の設計
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill imprecise-over-inaccurateインストール後、Claude Code で/imprecise-over-inaccurateを入力して読み込まれたか確認します。
詳細
不正確な型よりも、不厳密でも正確な型を優先する
概要
不厳密でも正確な型は、厳密でも不正確な型より優れています。
型をより厳密にすると、不正確になるリスクがあります。不正確な型は、誤検知(有効なコードが拒否される)や見逃し(無効なコードが受け入れられる)の原因になります。どちらも型システムへの信頼を損ないます。
このスキルを使う場面
- 型を「より厳密」にするとき
- 予期しない型エラーが発生するとき
- 有効なユースケースを拒否する型があるとき
- 複雑なジェネリック型をデバッグするとき
絶対的なルール
Types should never reject valid code.
When in doubt, be less precise.
覚えておきましょう:
- 誤検知(有効なコードを拒否すること)は信頼を損ないます
- 見逃し(無効なコードを受け入れること)はバグの原因になります
- 不厳密でも正確な型 > 厳密でも不正確な型
- 複雑な型はデバッグが困難です
検出:過度に厳密な型
// Attempt to precisely type CSS colors
type CSSColor =
| 'red' | 'blue' | 'green' | 'yellow' // named colors
| `#${string}` // hex colors
| `rgb(${number}, ${number}, ${number})`; // rgb
function setColor(color: CSSColor) { /* ... */ }
// But this valid CSS is rejected:
setColor('rgb(255,255,255)'); // Error: no spaces around commas!
setColor('rgba(0, 0, 0, 0.5)'); // Error: didn't account for rgba!
この厳密な型は不正確であり、有効な CSS を拒否します。
より良い方法:不厳密でも正確な型
// Less precise, but accepts all valid CSS colors
function setColor(color: string) { /* ... */ }
setColor('rgb(255, 255, 255)'); // OK
setColor('rgba(0, 0, 0, 0.5)'); // OK
setColor('invalid'); // OK (imprecise)
string 型は不厳密ですが(無効な色も受け入れます)、正確です(有効な色を拒否することはありません)。
中間案:部分的な厳密さ
// Provide known values, allow escape hatch
type CSSColor =
| 'red' | 'blue' | 'green' | 'yellow'
| (string & {}); // Allows any string with autocomplete for known values
setColor('red'); // Autocomplete works
setColor('custom'); // Still allowed
実例:JSON スキーマ
// Trying to precisely type JSON Schema
interface JSONSchema {
type: 'string' | 'number' | 'object' | 'array' | 'boolean' | 'null';
properties?: Record<string, JSONSchema>;
items?: JSONSchema;
// ... 50 more properties
}
// But real JSON Schema is more flexible:
const schema: JSONSchema = {
type: ['string', 'null'], // Error: type can be array!
// ...
};
「厳密な」型は不正確です。よりシンプルな型のほうが適している場合があります。
interface JSONSchema {
type?: string | string[];
[key: string]: unknown; // Allow any other properties
}
厳密さが役立つ場合
次の場合は、厳密にする価値があります。
- すべての入力を自分で管理している(内部型)
- 対象の範囲が明確に定義されている(有限で、値が既知)
- エラーを早期に検出できる(開発時)
- 型が十分にシンプルで、明らかに正しいと判断できる
// Good use of precision: finite, known values
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
// Good use of precision: simple domain
type PositiveInteger = number; // (Could use branded type)
厳密さを緩める場合
次の場合は、厳密さを緩めましょう。
- 外部データを扱う(API、ユーザー入力)
- 対象の範囲が複雑である(CSS、HTML、正規表現)
- 型が有効なユースケースを拒否する
- 型が複雑で、正しさを確認しにくい
// Over-precise: might reject valid CSS
type CSSLength = `${number}px` | `${number}em` | `${number}rem`;
// Better: trust the developer
type CSSLength = string;
// Or: document the expected format
/** CSS length value, e.g., "10px", "2em" */
type CSSLength = string;
誤検知と見逃しの比較
| 問題 | 影響 | 解決策 |
|---|---|---|
| 誤検知 | 有効なコードが拒否される | 型の厳密さを緩める |
| 見逃し | 無効なコードが受け入れられる | 実行時に検証する |
誤検知のほうが深刻です。理由は次のとおりです。
- 正当な作業を妨げる
- 回避策(
as any)が必要になる - TypeScript への信頼を損なう
段階的な調整
まずは不厳密に始め、必要に応じて厳密さを加えます。
// V1: Start broad
interface Config {
[key: string]: unknown;
}
// V2: Add known properties
interface Config {
port?: number;
host?: string;
[key: string]: unknown; // Still allow extras
}
// V3: Remove index signature if confident
interface Config {
port: number;
host: string;
}
圧力に対抗するための手順
1. 「より厳密なほうが良い」
圧力:「型はできるだけ具体的であるべきです」
**回答:**正確でもある場合に限ります。厳密でも誤っていれば、より悪い結果になります。
**対応:**確認しましょう。この厳密さによって、有効なコードが拒否されませんか?
2. 「すべてのエラーを捕捉すべき」
圧力:「型によって、すべての無効な値を防ぐべきです」
**回答:**すべてを型チェックすることはできません。実行時検証があります。
**対応:**正確に型付けできる部分に型を付け、残りは実行時に検証しましょう。
危険信号 — STOPして再検討しましょう
- ユニオン型やインターセクション型を多用した複雑な型
- 有効だと分かっているコードで型エラーが発生する
- 型を回避するために
as anyを頻繁に使う - 説明が難しい型
よくある正当化(すべて誤り)
| 言い訳 | 実態 |
|---|---|
| 「型としては技術的に正しい」 | 有効なコードを拒否するなら、正しくありません |
| 「ユーザーはもっと良いコードを書くべき」 | 型は理想ではなく、現実に合わせるべきです |
| 「例外を追加できる」 | 例外が必要なら、その型は間違っています |
クイックリファレンス
// DON'T: Over-precise type that rejects valid code
type Color = 'red' | 'blue' | `#${string}`;
setColor('rgb(0,0,0)'); // Error - but it's valid!
// DO: Imprecise type that accepts all valid code
type Color = string;
setColor('rgb(0,0,0)'); // OK
// BETTER: Partial precision with escape hatch
type Color = 'red' | 'blue' | (string & {});
setColor('red'); // Autocomplete works
setColor('rgb(0,0,0)'); // Still allowed
要点
厳密さより正確さが重要です。
無効な値を一部受け入れる型のほうが、有効な値を拒否する型より優れています。型をより厳密にする場合は、正確さが保たれていることを確認しましょう。厳密さによって誤検知が起きるなら、厳密さを緩めてください。
参考資料
Dan Vanderkam 著「Effective TypeScript」の項目 40「不正確な型よりも不厳密な型を優先する」に基づいています。