typescript-strict-mode

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TypeScript Strict Mode Best Practices

TypeScript严格模式最佳实践

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.
本技能涵盖适用于所有框架的严格TypeScript实践,重点在于避免使用
any
、使用正确的类型注解,以及利用TypeScript的类型系统编写更安全、更易维护的代码。

The Golden Rule: NEVER Use
any

黄金准则:绝不要使用
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
重要规则: 许多代码库已启用
@typescript-eslint/no-explicit-any
规则,使用
any
会导致构建失败。
为什么
any
很危险:
  • 违背了TypeScript类型系统的设计初衷
  • 隐藏了本可在编译阶段发现的bug
  • 在代码库中传播类型不安全问题
  • 使重构工作变得困难且容易出错

Alternatives to
any

any
的替代方案

1. Use Specific Types

1. 使用具体类型

❌ WRONG:
typescript
function processData(data: any) { ... }
const items: any[] = [];
✅ CORRECT:
typescript
function processData(data: { id: string; name: string }) { ... }
const items: string[] = [];
❌ 错误示例:
typescript
function processData(data: any) { ... }
const items: any[] = [];
✅ 正确示例:
typescript
function processData(data: { id: string; name: string }) { ... }
const items: string[] = [];

2. Use
unknown
When Type is Truly Unknown

2. 当类型真正未知时使用
unknown

unknown
is the type-safe counterpart to
any
. It forces you to narrow the type before using it.
❌ WRONG:
typescript
function handleResponse(response: any) {
  return response.data.name; // No type checking!
}
✅ CORRECT:
typescript
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");
}
unknown
any
的类型安全替代方案,它要求你在使用前先收窄类型范围。
❌ 错误示例:
typescript
function handleResponse(response: any) {
  return response.data.name; // 无类型检查!
}
✅ 正确示例:
typescript
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("响应格式无效");
}

3. Use Generics for Reusable Components

3. 为可复用组件使用泛型

❌ WRONG:
typescript
function wrapValue(value: any): { wrapped: any } {
  return { wrapped: value };
}
✅ CORRECT:
typescript
function wrapValue<T>(value: T): { wrapped: T } {
  return { wrapped: value };
}

// Usage
const wrappedString = wrapValue("hello"); // { wrapped: string }
const wrappedNumber = wrapValue(42); // { wrapped: number }
❌ 错误示例:
typescript
function wrapValue(value: any): { wrapped: any } {
  return { wrapped: value };
}
✅ 正确示例:
typescript
function wrapValue<T>(value: T): { wrapped: T } {
  return { wrapped: value };
}

// 使用示例
const wrappedString = wrapValue("hello"); // { wrapped: string }
const wrappedNumber = wrapValue(42); // { wrapped: number }

4. Use Union Types for Multiple Possibilities

4. 为多种可能的类型使用联合类型

❌ WRONG:
typescript
function handleInput(input: any) {
  if (typeof input === 'string') { ... }
  if (typeof input === 'number') { ... }
}
✅ CORRECT:
typescript
function handleInput(input: string | number) {
  if (typeof input === 'string') { ... }
  if (typeof input === 'number') { ... }
}
❌ 错误示例:
typescript
function handleInput(input: any) {
  if (typeof input === 'string') { ... }
  if (typeof input === 'number') { ... }
}
✅ 正确示例:
typescript
function handleInput(input: string | number) {
  if (typeof input === 'string') { ... }
  if (typeof input === 'number') { ... }
}

5. Use Type Guards for Runtime Checks

5. 使用类型守卫进行运行时检查

typescript
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);
  }
}
typescript
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的类型为User
    console.log(data.name);
  }
}

6. Use
Record<K, V>
for Dynamic Objects

6. 为动态对象使用
Record<K, V>

❌ WRONG:
typescript
const cache: any = {};
cache["key"] = "value";
✅ CORRECT:
typescript
const cache: Record<string, string> = {};
cache["key"] = "value";

// Or with specific keys
const userSettings: Record<"theme" | "language", string> = {
  theme: "dark",
  language: "en",
};
❌ 错误示例:
typescript
const cache: any = {};
cache["key"] = "value";
✅ 正确示例:
typescript
const cache: Record<string, string> = {};
cache["key"] = "value";

// 或者使用特定键
const userSettings: Record<"theme" | "language", string> = {
  theme: "dark",
  language: "en",
};

7. Use Index Signatures for Flexible Objects

7. 为灵活对象使用索引签名

typescript
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,
};
typescript
interface Config {
  name: string;
  version: string;
  [key: string]: string | number | boolean; // 额外属性
}

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

Common Event Handler Types

常见事件处理程序类型

React Event Types

React事件类型

typescript
// Form events
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
  e.preventDefault();
  // ...
};

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

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

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

// Focus events
const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
  // ...
};
typescript
// 表单事件
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
  e.preventDefault();
  // ...
};

// 输入事件
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  const value = e.target.value;
  // ...
};

// 点击事件
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
  // ...
};

// 键盘事件
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
  if (e.key === 'Enter') { ... }
};

// 焦点事件
const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
  // ...
};

DOM Event Types (Non-React)

DOM事件类型(非React)

typescript
// Generic DOM events
document.addEventListener('click', (e: MouseEvent) => { ... });
document.addEventListener('keydown', (e: KeyboardEvent) => { ... });
document.addEventListener('submit', (e: SubmitEvent) => { ... });
typescript
// 通用DOM事件
document.addEventListener('click', (e: MouseEvent) => { ... });
document.addEventListener('keydown', (e: KeyboardEvent) => { ... });
document.addEventListener('submit', (e: SubmitEvent) => { ... });

Promise and Async Types

Promise与异步类型

Typing Async Functions

为异步函数添加类型

typescript
// Function returning a promise
async function fetchUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

// Arrow function variant
const fetchUser = async (id: string): Promise<User> => {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
};
typescript
// 返回Promise的函数
async function fetchUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

// 箭头函数版本
const fetchUser = async (id: string): Promise<User> => {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
};

Promise Type Patterns

Promise类型模式

typescript
// Promise with explicit type
const userPromise: Promise<User> = 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[]]
typescript
// 带显式类型的Promise
const userPromise: Promise<User> = fetchUser("123");

// 带类型推断的await
const user = await fetchUser("123"); // User类型

// 多类型的Promise.all
const [user, posts] = await Promise.all([fetchUser("123"), fetchPosts("123")]); // [User, Post[]]类型

Function Types

函数类型

Callback Types

回调类型

typescript
// 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);
}
typescript
// 带类型的回调参数
function processItems(
  items: string[],
  callback: (item: string, index: number) => void
) {
  items.forEach(callback);
}

// 替代方案:提取类型
type ItemCallback = (item: string, index: number) => void;

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

Overloaded Functions

重载函数

typescript
// 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());
}
typescript
// 针对不同输入/输出类型的函数重载
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:
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.
仅当你比TypeScript更了解类型时才使用类型断言:
typescript
// DOM元素断言(当你知道元素类型时)
const input = document.getElementById("email") as HTMLInputElement;

// 响应数据断言(当你信任API时)
const data = (await response.json()) as ApiResponse;

// 非空断言(当你知道它不为null时)
const element = document.querySelector(".button")!;
警告: 类型断言会绕过TypeScript的检查,尽可能优先使用类型守卫。

Utility Types

工具类型

Built-in Utility Types

内置工具类型

typescript
// Partial - all properties optional
type PartialUser = Partial<User>;

// Required - all properties required
type RequiredUser = Required<User>;

// Pick - select specific properties
type UserName = Pick<User, "name" | "email">;

// Omit - exclude specific properties
type UserWithoutId = Omit<User, "id">;

// Readonly - immutable properties
type ReadonlyUser = Readonly<User>;

// Record - create object type
type UserMap = Record<string, User>;

// ReturnType - extract function return type
type FetchUserReturn = ReturnType<typeof fetchUser>;

// Parameters - extract function parameters
type FetchUserParams = Parameters<typeof fetchUser>;
typescript
// Partial - 所有属性可选
type PartialUser = Partial<User>;

// Required - 所有属性必填
type RequiredUser = Required<User>;

// Pick - 选择特定属性
type UserName = Pick<User, "name" | "email">;

// Omit - 排除特定属性
type UserWithoutId = Omit<User, "id">;

// Readonly - 属性不可变
type ReadonlyUser = Readonly<User>;

// Record - 创建对象类型
type UserMap = Record<string, User>;

// ReturnType - 提取函数返回类型
type FetchUserReturn = ReturnType<typeof fetchUser>;

// Parameters - 提取函数参数
type FetchUserParams = Parameters<typeof fetchUser>;

Discriminated Unions

可辨识联合类型

Pattern for handling multiple related types:
typescript
type Result<T> = { success: true; data: T } | { success: false; error: string };

function handleResult<T>(result: Result<T>) {
  if (result.success) {
    // TypeScript knows result.data exists here
    console.log(result.data);
  } else {
    // TypeScript knows result.error exists here
    console.error(result.error);
  }
}
处理多个相关类型的模式:
typescript
type Result<T> = { success: true; data: T } | { success: false; error: string };

function handleResult<T>(result: Result<T>) {
  if (result.success) {
    // TypeScript知道此处result.data存在
    console.log(result.data);
  } else {
    // TypeScript知道此处result.error存在
    console.error(result.error);
  }
}

Module Augmentation

模块扩展

Extend existing types without modifying original:
typescript
// 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;
    }
  }
}
无需修改原始代码即可扩展现有类型:
typescript
// 扩展Express Request
declare module "express" {
  interface Request {
    user?: User;
  }
}

// 扩展环境变量
declare global {
  namespace NodeJS {
    interface ProcessEnv {
      DATABASE_URL: string;
      API_KEY: string;
    }
  }
}

Common Pitfalls

常见陷阱

Pitfall 1: Using
any
for JSON Data

陷阱1:为JSON数据使用
any

❌ WRONG:
typescript
const data: any = JSON.parse(jsonString);
✅ CORRECT:
typescript
interface ExpectedData {
  id: string;
  name: string;
}

const data: unknown = JSON.parse(jsonString);
// Then validate with type guard or schema validation (zod, etc.)
❌ 错误示例:
typescript
const data: any = JSON.parse(jsonString);
✅ 正确示例:
typescript
interface ExpectedData {
  id: string;
  name: string;
}

const data: unknown = JSON.parse(jsonString);
// 然后使用类型守卫或Schema验证(如zod等)进行校验

Pitfall 2: Implicit
any
in Callbacks

陷阱2:回调中的隐式
any

❌ WRONG:
typescript
// 'item' has implicit 'any' type
items.map((item) => item.name);
✅ CORRECT:
typescript
items.map((item: Item) => item.name);
// Or ensure 'items' has proper type: Item[]
❌ 错误示例:
typescript
// 'item'具有隐式'any'类型
items.map((item) => item.name);
✅ 正确示例:
typescript
items.map((item: Item) => item.name);
// 或者确保'items'具有正确类型:Item[]

Pitfall 3: Object Property Access

陷阱3:对象属性访问

❌ WRONG:
typescript
function getValue(obj: any, key: string) {
  return obj[key];
}
✅ CORRECT:
typescript
function getValue<T extends Record<string, unknown>, K extends keyof T>(
  obj: T,
  key: K
): T[K] {
  return obj[key];
}
❌ 错误示例:
typescript
function getValue(obj: any, key: string) {
  return obj[key];
}
✅ 正确示例:
typescript
function getValue<T extends Record<string, unknown>, K extends keyof T>(
  obj: T,
  key: K
): T[K] {
  return obj[key];
}

Pitfall 4: Empty Array Type

陷阱4:空数组类型

❌ WRONG:
typescript
const items = []; // any[]
✅ CORRECT:
typescript
const items: string[] = [];
// or
const items: Array<string> = [];
❌ 错误示例:
typescript
const items = []; // any[]类型
✅ 正确示例:
typescript
const items: string[] = [];
// 或者
const items: Array<string> = [];

ESLint Rules to Enable

需启用的ESLint规则

For strict TypeScript, enable these rules:
json
{
  "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
.
对于严格TypeScript,启用以下规则:
json
{
  "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"
  }
}
注意: 替代已废弃的
@typescript-eslint/no-implicit-any-catch
规则,在
tsconfig.json
中设置
useUnknownInCatchVariables: true
(TypeScript 4.4+)。这会确保catch子句的变量类型为
unknown
而非
any

Quick Reference

快速参考

| Instead of
any
| Use | | ---------------- | ------------------------ | --- | | Unknown data |
unknown
| | Flexible type | Generics
<T>
| | Multiple types | Union
A                 | B
| | Dynamic keys |
Record<K, V>
| | Nullable |
T \| null
| | Optional |
T \| undefined
or
T?
| | Callback |
(args) => ReturnType
| | Empty array |
Type[]
| | JSON data |
unknown
+ type guard |
替代
any
的方案
使用类型
未知数据
unknown
灵活类型泛型
<T>
多种类型联合类型`A
动态键
Record<K, V>
可空类型`T
可选类型`T
回调函数
(args) => ReturnType
空数组
Type[]
JSON数据
unknown
+ 类型守卫

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
  • 绝不要使用
    any
    - 它违背了TypeScript的设计初衷
  • 使用
    unknown
    处理真正未知的类型,然后通过类型守卫收窄范围
  • 使用泛型 编写可复用的类型安全组件
  • 使用联合类型 处理有限的多种可能类型
  • 使用可辨识联合类型 处理复杂状态机
  • 启用严格的ESLint规则 自动捕获违规情况