typescript-strict-mode

安装量: 79
排名: #9893

安装

npx skills add https://github.com/fluid-tools/claude-skills --skill typescript-strict-mode

TypeScript Strict Mode Best Practices Overview

This skill covers strict TypeScript practices applicable across all frameworks. It focuses on avoiding any, using proper type annotations, and leveraging TypeScript's type system for safer, more maintainable code.

The Golden Rule: NEVER Use any

CRITICAL RULE: Many codebases have @typescript-eslint/no-explicit-any enabled. Using any will cause build failures.

Why any is dangerous:

Defeats the purpose of TypeScript's type system Hides bugs that would be caught at compile time Propagates type unsafety through the codebase Makes refactoring difficult and error-prone Alternatives to any 1. Use Specific Types

❌ WRONG:

function processData(data: any) { ... } const items: any[] = [];

✅ CORRECT:

function processData(data: { id: string; name: string }) { ... } const items: string[] = [];

  1. Use unknown When Type is Truly Unknown

unknown is the type-safe counterpart to any. It forces you to narrow the type before using it.

❌ WRONG:

function handleResponse(response: any) { return response.data.name; // No type checking! }

✅ CORRECT:

function handleResponse(response: unknown) { if ( typeof response === "object" && response !== null && "data" in response && typeof (response as { data: unknown }).data === "object" ) { const data = (response as { data: { name: string } }).data; return data.name; } throw new Error("Invalid response format"); }

  1. Use Generics for Reusable Components

❌ WRONG:

function wrapValue(value: any): { wrapped: any } { return { wrapped: value }; }

✅ CORRECT:

function wrapValue(value: T): { wrapped: T } { return { wrapped: value }; }

// Usage const wrappedString = wrapValue("hello"); // { wrapped: string } const wrappedNumber = wrapValue(42); // { wrapped: number }

  1. Use Union Types for Multiple Possibilities

❌ WRONG:

function handleInput(input: any) { if (typeof input === 'string') { ... } if (typeof input === 'number') { ... } }

✅ CORRECT:

function handleInput(input: string | number) { if (typeof input === 'string') { ... } if (typeof input === 'number') { ... } }

  1. Use Type Guards for Runtime Checks interface User { id: string; name: string; email: string; }

function isUser(value: unknown): value is User { return ( typeof value === "object" && value !== null && "id" in value && "name" in value && "email" in value && typeof (value as User).id === "string" && typeof (value as User).name === "string" && typeof (value as User).email === "string" ); }

function processUser(data: unknown) { if (isUser(data)) { // data is now typed as User console.log(data.name); } }

  1. Use Record for Dynamic Objects

❌ WRONG:

const cache: any = {}; cache["key"] = "value";

✅ CORRECT:

const cache: Record = {}; cache["key"] = "value";

// Or with specific keys const userSettings: Record<"theme" | "language", string> = { theme: "dark", language: "en", };

  1. Use Index Signatures for Flexible Objects interface Config { name: string; version: string; [key: string]: string | number | boolean; // Additional properties }

const config: Config = { name: "my-app", version: "1.0.0", debug: true, port: 3000, };

Common Event Handler Types React Event Types // Form events const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); // ... };

// Input events const handleChange = (e: React.ChangeEvent) => { const value = e.target.value; // ... };

// Click events const handleClick = (e: React.MouseEvent) => { // ... };

// Keyboard events const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { ... } };

// Focus events const handleFocus = (e: React.FocusEvent) => { // ... };

DOM Event Types (Non-React) // Generic DOM events document.addEventListener('click', (e: MouseEvent) => { ... }); document.addEventListener('keydown', (e: KeyboardEvent) => { ... }); document.addEventListener('submit', (e: SubmitEvent) => { ... });

Promise and Async Types Typing Async Functions // Function returning a promise async function fetchUser(id: string): Promise { const response = await fetch(/api/users/${id}); return response.json(); }

// Arrow function variant const fetchUser = async (id: string): Promise => { const response = await fetch(/api/users/${id}); return response.json(); };

Promise Type Patterns // Promise with explicit type const userPromise: Promise = fetchUser("123");

// Awaiting with type inference const user = await fetchUser("123"); // User

// Promise.all with multiple types const [user, posts] = await Promise.all([fetchUser("123"), fetchPosts("123")]); // [User, Post[]]

Function Types Callback Types // Typed callback parameter function processItems( items: string[], callback: (item: string, index: number) => void ) { items.forEach(callback); }

// Alternative: Extract the type type ItemCallback = (item: string, index: number) => void;

function processItems(items: string[], callback: ItemCallback) { items.forEach(callback); }

Overloaded Functions // Function overloads for different input/output types function parse(input: string): object; function parse(input: Buffer): object; function parse(input: string | Buffer): object { if (typeof input === "string") { return JSON.parse(input); } return JSON.parse(input.toString()); }

Type Assertions (Use Sparingly)

Use type assertions only when you know more than TypeScript:

// DOM element assertion (when you know the element type) const input = document.getElementById("email") as HTMLInputElement;

// Response data assertion (when you trust the API) const data = (await response.json()) as ApiResponse;

// Non-null assertion (when you know it's not null) const element = document.querySelector(".button")!;

Warning: Type assertions bypass TypeScript's checks. Prefer type guards when possible.

Utility Types Built-in Utility Types // Partial - all properties optional type PartialUser = Partial;

// Required - all properties required type RequiredUser = Required;

// Pick - select specific properties type UserName = Pick;

// Omit - exclude specific properties type UserWithoutId = Omit;

// Readonly - immutable properties type ReadonlyUser = Readonly;

// Record - create object type type UserMap = Record;

// ReturnType - extract function return type type FetchUserReturn = ReturnType;

// Parameters - extract function parameters type FetchUserParams = Parameters;

Discriminated Unions

Pattern for handling multiple related types:

type Result = { success: true; data: T } | { success: false; error: string };

function handleResult(result: Result) { if (result.success) { // TypeScript knows result.data exists here console.log(result.data); } else { // TypeScript knows result.error exists here console.error(result.error); } }

Module Augmentation

Extend existing types without modifying original:

// Extend Express Request declare module "express" { interface Request { user?: User; } }

// Extend environment variables declare global { namespace NodeJS { interface ProcessEnv { DATABASE_URL: string; API_KEY: string; } } }

Common Pitfalls Pitfall 1: Using any for JSON Data

❌ WRONG:

const data: any = JSON.parse(jsonString);

✅ CORRECT:

interface ExpectedData { id: string; name: string; }

const data: unknown = JSON.parse(jsonString); // Then validate with type guard or schema validation (zod, etc.)

Pitfall 2: Implicit any in Callbacks

❌ WRONG:

// 'item' has implicit 'any' type items.map((item) => item.name);

✅ CORRECT:

items.map((item: Item) => item.name); // Or ensure 'items' has proper type: Item[]

Pitfall 3: Object Property Access

❌ WRONG:

function getValue(obj: any, key: string) { return obj[key]; }

✅ CORRECT:

function getValue, K extends keyof T>( obj: T, key: K ): T[K] { return obj[key]; }

Pitfall 4: Empty Array Type

❌ WRONG:

const items = []; // any[]

✅ CORRECT:

const items: string[] = []; // or const items: Array = [];

ESLint Rules to Enable

For strict TypeScript, enable these rules:

{ "rules": { "@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/strict-boolean-expressions": "warn", "@typescript-eslint/no-unsafe-assignment": "error", "@typescript-eslint/no-unsafe-member-access": "error", "@typescript-eslint/no-unsafe-call": "error", "@typescript-eslint/no-unsafe-return": "error" } }

Note: Instead of the deprecated @typescript-eslint/no-implicit-any-catch rule, set useUnknownInCatchVariables: true in your tsconfig.json (TypeScript 4.4+). This ensures catch clause variables are typed as unknown instead of any.

Quick Reference

| Instead of any | Use | | ---------------- | ------------------------ | --- | | Unknown data | unknown | | Flexible type | Generics | | Multiple types | Union A | B | | Dynamic keys | Record | | Nullable | T | null | | Optional | T | undefined or T? | | Callback | (args) => ReturnType | | Empty array | Type[] | | JSON data | unknown + type guard |

Summary Never use any - it defeats TypeScript's purpose Use unknown for truly unknown types, then narrow with type guards Use generics for reusable, type-safe components Use union types for finite sets of possibilities Use discriminated unions for complex state machines Enable strict ESLint rules to catch violations automatically

返回排行榜