strict-typing について
strict-typingは、TypeScript、Python、Go、Rust、Java、C#などを使う開発者向けのコーディングスキルです。型の逃げ道を避け、明示的な型指定、厳格な設定、実行時検証を促します。
主な機能
- 01anyやunknownなどの型逃れを回避
- 02関数の引数と戻り値に型を指定
- 03厳格な型チェック設定を推奨
- 04動的データの形状を定義して検証
- 05型定義のないライブラリに定義を追加
活用シーン
- 01型安全なTypeScriptやPythonの記述
- 02API応答や解析済みJSONの処理
- 03型宣言のないサードパーティライブラリの利用
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ troykelly/ claude-skills --skill strict-typingインストール後、Claude Code で/strict-typingを入力して読み込まれたか確認します。
詳細
厳密な型付け
概要
any 型は使用しません。unknown を型ガードなしで使用しません。すべてを完全に型付けします。
基本原則: 型はコンパイラーが検証するドキュメントです。
このスキルの対象: TypeScript、Python(型ヒントを使用)、Go、Rust、Java、C#、および型付けが可能なすべての言語。
ルール
NEVER use any, unknown, or equivalent type escapes.
ALWAYS provide explicit, accurate types.
TAKE EXTRA TIME if needed to type correctly.
TypeScript の詳細
禁止するパターン
// NEVER
const data: any = fetchData();
const items: unknown[] = parseItems();
function process(input: any): any { }
const config = {} as any;
// @ts-ignore
// @ts-expect-error (unless truly necessary with documentation)
必須のパターン
// ALWAYS
interface UserData {
id: string;
name: string;
email: string;
}
const data: UserData = fetchData();
function process<T extends Processable>(input: T): ProcessResult<T> {
// ...
}
設定
tsconfig.json で strict モードが有効になっていることを確認します。
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noUncheckedIndexedAccess": true
}
}
サードパーティの型の扱い
ライブラリーの型がない場合:
// Create type definitions
declare module 'untyped-library' {
export interface Config {
option1: string;
option2: number;
}
export function init(config: Config): void;
}
または、DefinitelyTyped に型を提供します。
動的データの扱い
API レスポンスや解析した JSON の場合:
// Define expected shape
interface ApiResponse {
users: User[];
pagination: Pagination;
}
// Use type guard for runtime validation
function isApiResponse(data: unknown): data is ApiResponse {
return (
typeof data === 'object' &&
data !== null &&
'users' in data &&
Array.isArray((data as ApiResponse).users)
);
}
// Use with validation
const response = await fetch('/api/users');
const data: unknown = await response.json();
if (!isApiResponse(data)) {
throw new Error('Invalid API response');
}
// data is now typed as ApiResponse
実行時検証に Zod を使用する
import { z } from 'zod';
const UserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
});
type User = z.infer<typeof UserSchema>;
// Parse and validate
const user = UserSchema.parse(unknownData);
// user is now typed as User
Python の詳細
禁止するパターン
# NEVER
def process(data): # Missing type hints
pass
def fetch() -> Any: # Using Any
pass
from typing import Any
result: Any = compute()
必須のパターン
# ALWAYS
from typing import TypeVar, Generic, Protocol
from dataclasses import dataclass
@dataclass
class User:
id: str
name: str
email: str
def process(data: User) -> ProcessResult:
...
T = TypeVar('T', bound='Processable')
def transform(items: list[T]) -> list[T]:
...
設定
厳密な mypy 設定を使用します。
# mypy.ini
[mypy]
strict = True
disallow_any_generics = True
disallow_untyped_defs = True
disallow_incomplete_defs = True
check_untyped_defs = True
disallow_untyped_decorators = True
warn_redundant_casts = True
warn_unused_ignores = True
Go の詳細
Go は静的型付け言語ですが、次の使用は避けます。
// AVOID
interface{} // empty interface
any // Go 1.18+ alias for interface{}
// PREFER
type specific interfaces or concrete types
interface{} が本当に必要な場合は、その理由を記録し、型アサーションを追加します。
型付けが難しい場合
型付けが不可能に思える場合:
ステップ 1:設計を見直す
Is the type hard to express because the design is complex?
→ Consider simplifying the design
ステップ 2:ジェネリクスを使用する
// Instead of any
function process<T>(input: T): T {
return input;
}
ステップ 3:ユニオン型を使用する
// Instead of any for multiple types
type Input = string | number | User;
function process(input: Input): void {
if (typeof input === 'string') {
// input is string
} else if (typeof input === 'number') {
// input is number
} else {
// input is User
}
}
ステップ 4:型ガードを作成する
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value
);
}
ステップ 5:記録して正当化する(最終手段)
any が本当に避けられない場合(極めてまれです):
// JUSTIFIED: Third-party library `foo` has no types and
// creating accurate types requires reverse-engineering
// the entire library. See issue #123 for type contribution.
// TODO(#456): Remove when @types/foo is available
const result: any = thirdPartyCall();
このようなケースは例外的にまれであるべきです。
所要時間
適切な型付けには時間がかかります。それで問題ありません。
| 状況 | 許容される時間 |
|---|---|
| シンプルなインターフェース | 5 分 |
| 複雑なジェネリック | 30 分 |
| 型ガード | 15 分 |
| ライブラリーの型 | 1 時間 |
型付けにさらに時間がかかる場合は、設計を見直す必要があるかもしれません。
チェックリスト
コードをコミットする前に:
-
any型を使用していない - 型ガードのない
unknownを使用していない -
@ts-ignoreまたは# type: ignoreを使用していない - すべての関数のパラメーターに型を付けている
- すべての関数の戻り値に型を付けている
- 公開するすべてのインターフェース/型をエクスポートしている
- 型設定が厳密である
よくある言い訳への回答
| 言い訳 | 回答 |
|---|---|
| 「一時的なものです」 | 一時的なコードは恒久的なものになります。今すぐ型を付けましょう。 |
| 「型は後で修正します」 | 後でその機会は訪れません。今すぐ型を付けましょう。 |
「any のほうが速いです」 | 技術的負債によって遅くなります。今すぐ型を付けましょう。 |
| 「ライブラリーに型がありません」 | 型を作成するか、Zod を使用しましょう。 |
| 「複雑すぎて型を付けられません」 | 設計をシンプルにしましょう。 |
統合
このスキルは次によって適用されます:
issue-driven-development- ステップ 7
このスキルによって、次のことが実現します:
- 自己文書化コード
- コンパイル時のエラー検出
- 安全なリファクタリング
- IDE のサポート向上
コメント
Markdown 対応