typescript-strict
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTypeScript Strict Mode
TypeScript 严格模式
Catch errors at build time, not in production.
在构建阶段而非生产阶段捕获错误。
When to Use This Skill
何时使用该技能
- Starting a new TypeScript project
- Tightening an existing codebase
- Preventing types from leaking through
any - Want compile-time guarantees for runtime safety
- 启动新的TypeScript项目
- 收紧现有代码库的类型检查
- 防止类型渗透
any - 希望为运行时安全提供编译阶段保障
Core Concepts
核心概念
- Strict mode - Enables all strict type checks
- Index safety - Array access returns
T | undefined - Exhaustiveness - Compiler ensures all cases handled
- Branded types - Prevent mixing up IDs and primitives
- Strict mode - 启用所有严格类型检查
- 索引安全 - 数组访问返回
T | undefined - 穷尽式检查 - 编译器确保所有情况都被处理
- Branded类型 - 避免混淆ID和原始类型
TypeScript Implementation
TypeScript 实现
tsconfig.json (Strict Configuration)
tsconfig.json(严格配置)
json
{
"compilerOptions": {
// Target modern JS
"target": "ES2022",
"lib": ["ES2022"],
"module": "ESNext",
"moduleResolution": "bundler",
// STRICT MODE - The important part
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"exactOptionalPropertyTypes": true,
// Additional safety
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
// Interop
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
// Output
"declaration": true,
"declarationMap": true,
"sourceMap": true
}
}json
{
"compilerOptions": {
// 目标现代JS
"target": "ES2022",
"lib": ["ES2022"],
"module": "ESNext",
"moduleResolution": "bundler",
// STRICT MODE - 核心配置
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"exactOptionalPropertyTypes": true,
// 额外安全配置
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
// 互操作性
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
// 输出配置
"declaration": true,
"declarationMap": true,
"sourceMap": true
}
}What Each Flag Does
各标志的作用
| Flag | Effect |
|---|---|
| Enables all strict type checks |
| |
| Must use |
| |
| All code paths must return |
| Require break/return in switch |
| 标志 | 效果 |
|---|---|
| 启用所有严格类型检查 |
| |
| 必须使用 |
| |
| 所有代码路径必须有返回值 |
| Switch语句中必须使用break/return |
Path Aliases
路径别名
json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@/components/*": ["./src/components/*"],
"@/lib/*": ["./src/lib/*"],
"@/types/*": ["./src/types/*"]
}
}
}typescript
// Before (fragile)
import { Button } from '../../../components/ui/Button';
// After (clean)
import { Button } from '@/components/ui/Button';json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@/components/*": ["./src/components/*"],
"@/lib/*": ["./src/lib/*"],
"@/types/*": ["./src/types/*"]
}
}
}typescript
// 之前(脆弱的写法)
import { Button } from '../../../components/ui/Button';
// 之后(简洁的写法)
import { Button } from '@/components/ui/Button';Type Patterns
类型模式
Branded Types (Prevent ID Mixups)
Branded类型(避免ID混淆)
typescript
// types/branded.ts
declare const brand: unique symbol;
type Brand<T, B> = T & { [brand]: B };
export type UserId = Brand<string, 'UserId'>;
export type OrderId = Brand<string, 'OrderId'>;
export type ProductId = Brand<string, 'ProductId'>;
// Helper to create branded values
export const UserId = (id: string) => id as UserId;
export const OrderId = (id: string) => id as OrderId;
// Usage - compiler prevents mixing IDs
function getOrder(id: OrderId): Promise<Order>;
function getUser(id: UserId): Promise<User>;
const userId = UserId('user_123');
const orderId = OrderId('order_456');
getUser(userId); // ✅ OK
getOrder(orderId); // ✅ OK
getOrder(userId); // ❌ Type error! Can't use UserId as OrderIdtypescript
// types/branded.ts
declare const brand: unique symbol;
type Brand<T, B> = T & { [brand]: B };
export type UserId = Brand<string, 'UserId'>;
export type OrderId = Brand<string, 'OrderId'>;
export type ProductId = Brand<string, 'ProductId'>;
// 创建Branded值的工具函数
export const UserId = (id: string) => id as UserId;
export const OrderId = (id: string) => id as OrderId;
// 使用示例 - 编译器会阻止ID混用
function getOrder(id: OrderId): Promise<Order>;
function getUser(id: UserId): Promise<User>;
const userId = UserId('user_123');
const orderId = OrderId('order_456');
getUser(userId); // ✅ 合法
getOrder(orderId); // ✅ 合法
getOrder(userId); // ❌ 类型错误!不能将UserId用作OrderIdExhaustive Switch
穷尽式Switch
typescript
// Ensure all enum cases handled
type Status = 'pending' | 'active' | 'completed' | 'failed';
function getStatusColor(status: Status): string {
switch (status) {
case 'pending':
return 'yellow';
case 'active':
return 'blue';
case 'completed':
return 'green';
case 'failed':
return 'red';
default:
// This line ensures exhaustiveness
const _exhaustive: never = status;
throw new Error(`Unhandled status: ${_exhaustive}`);
}
}
// If you add a new status, TypeScript will error until you handle ittypescript
// 确保所有枚举情况都被处理
type Status = 'pending' | 'active' | 'completed' | 'failed';
function getStatusColor(status: Status): string {
switch (status) {
case 'pending':
return 'yellow';
case 'active':
return 'blue';
case 'completed':
return 'green';
case 'failed':
return 'red';
default:
// 这一行确保穷尽性检查
const _exhaustive: never = status;
throw new Error(`未处理的状态: ${_exhaustive}`);
}
}
// 如果你添加了新的状态,TypeScript会报错直到你处理它Result Type (No Exceptions)
Result类型(无异常抛出)
typescript
// types/result.ts
export type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
export function ok<T>(value: T): Result<T, never> {
return { ok: true, value };
}
export function err<E>(error: E): Result<never, E> {
return { ok: false, error };
}
// Usage
type FetchError = 'NOT_FOUND' | 'NETWORK_ERROR' | 'UNAUTHORIZED';
async function fetchUser(id: string): Promise<Result<User, FetchError>> {
try {
const response = await fetch(`/api/users/${id}`);
if (response.status === 404) return err('NOT_FOUND');
if (response.status === 401) return err('UNAUTHORIZED');
if (!response.ok) return err('NETWORK_ERROR');
const user = await response.json();
return ok(user);
} catch {
return err('NETWORK_ERROR');
}
}
// Caller must handle both cases
const result = await fetchUser('123');
if (!result.ok) {
// TypeScript knows: result.error is FetchError
switch (result.error) {
case 'NOT_FOUND':
return notFound();
case 'UNAUTHORIZED':
return redirect('/login');
case 'NETWORK_ERROR':
return serverError();
}
}
// TypeScript knows: result.value is User
console.log(result.value.name);typescript
// types/result.ts
export type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
export function ok<T>(value: T): Result<T, never> {
return { ok: true, value };
}
export function err<E>(error: E): Result<never, E> {
return { ok: false, error };
}
// 使用示例
type FetchError = 'NOT_FOUND' | 'NETWORK_ERROR' | 'UNAUTHORIZED';
async function fetchUser(id: string): Promise<Result<User, FetchError>> {
try {
const response = await fetch(`/api/users/${id}`);
if (response.status === 404) return err('NOT_FOUND');
if (response.status === 401) return err('UNAUTHORIZED');
if (!response.ok) return err('NETWORK_ERROR');
const user = await response.json();
return ok(user);
} catch {
return err('NETWORK_ERROR');
}
}
// 调用者必须处理两种情况
const result = await fetchUser('123');
if (!result.ok) {
// TypeScript知道: result.error是FetchError
switch (result.error) {
case 'NOT_FOUND':
return notFound();
case 'UNAUTHORIZED':
return redirect('/login');
case 'NETWORK_ERROR':
return serverError();
}
}
// TypeScript知道: result.value是User
console.log(result.value.name);Type Guards
类型守卫
typescript
// Type guard function
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'email' in value &&
typeof (value as User).id === 'string' &&
typeof (value as User).email === 'string'
);
}
// Usage with unknown data
function handleWebhook(body: unknown) {
if (isUser(body)) {
// TypeScript knows body is User here
console.log(body.email);
}
}typescript
// 类型守卫函数
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'email' in value &&
typeof (value as User).id === 'string' &&
typeof (value as User).email === 'string'
);
}
// 处理未知数据时的用法
function handleWebhook(body: unknown) {
if (isUser(body)) {
// TypeScript知道此处body是User类型
console.log(body.email);
}
}Zod for Runtime Validation
Zod 运行时校验
typescript
// schemas/user.ts
import { z } from 'zod';
export const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().min(1).max(100),
role: z.enum(['admin', 'user', 'guest']),
createdAt: z.string().datetime(),
});
// Infer TypeScript type from schema
export type User = z.infer<typeof UserSchema>;
// Validate external data
function handleWebhook(body: unknown): User {
return UserSchema.parse(body); // Throws ZodError if invalid
}
// Safe parse (no throw)
const result = UserSchema.safeParse(body);
if (!result.success) {
console.error(result.error.issues);
return;
}
// result.data is Usertypescript
// schemas/user.ts
import { z } from 'zod';
export const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().min(1).max(100),
role: z.enum(['admin', 'user', 'guest']),
createdAt: z.string().datetime(),
});
// 从Schema推断TypeScript类型
export type User = z.infer<typeof UserSchema>;
// 校验外部数据
function handleWebhook(body: unknown): User {
return UserSchema.parse(body); // 如果无效会抛出ZodError
}
// 安全校验(不抛出异常)
const result = UserSchema.safeParse(body);
if (!result.success) {
console.error(result.error.issues);
return;
}
// result.data是User类型Common Strict Mode Fixes
常见严格模式修复方案
Fix 1: Array Index Access
修复1:数组索引访问
typescript
// ❌ Error with noUncheckedIndexedAccess
const items = ['a', 'b', 'c'];
const first = items[0].toUpperCase(); // items[0] is string | undefined
// ✅ Fix: Optional chaining
const first = items[0]?.toUpperCase();
// ✅ Fix: Check first
const first = items[0];
if (first) {
console.log(first.toUpperCase());
}
// ✅ Fix: Non-null assertion (when you're certain)
const first = items[0]!.toUpperCase();
// ✅ Fix: Use .at() with check
const first = items.at(0);
if (first !== undefined) {
console.log(first.toUpperCase());
}typescript
// ❌ 开启noUncheckedIndexedAccess时会报错
const items = ['a', 'b', 'c'];
const first = items[0].toUpperCase(); // items[0]是string | undefined类型
// ✅ 修复:可选链
const first = items[0]?.toUpperCase();
// ✅ 修复:先检查
const first = items[0];
if (first) {
console.log(first.toUpperCase());
}
// ✅ 修复:非空断言(当你确定存在时)
const first = items[0]!.toUpperCase();
// ✅ 修复:使用.at()并检查
const first = items.at(0);
if (first !== undefined) {
console.log(first.toUpperCase());
}Fix 2: Optional Properties
修复2:可选属性
typescript
// ❌ Error with exactOptionalPropertyTypes
interface Config {
timeout?: number;
}
const config: Config = { timeout: undefined }; // Error!
// ✅ Fix: Omit the property
const config: Config = {};
// ✅ Fix: Explicitly allow undefined
interface Config {
timeout?: number | undefined;
}typescript
// ❌ 开启exactOptionalPropertyTypes时会报错
interface Config {
timeout?: number;
}
const config: Config = { timeout: undefined }; // 错误!
// ✅ 修复:省略该属性
const config: Config = {};
// ✅ 修复:显式允许undefined
interface Config {
timeout?: number | undefined;
}Fix 3: Object Index Signature
修复3:对象索引签名
typescript
// ❌ Error with noPropertyAccessFromIndexSignature
interface Cache {
[key: string]: string;
}
const cache: Cache = {};
const value = cache.someKey; // Error: use bracket notation
// ✅ Fix: Use bracket notation
const value = cache['someKey'];typescript
// ❌ 开启noPropertyAccessFromIndexSignature时会报错
interface Cache {
[key: string]: string;
}
const cache: Cache = {};
const value = cache.someKey; // 错误:使用方括号语法
// ✅ 修复:使用方括号语法
const value = cache['someKey'];Fix 4: Override Keyword
修复4:Override关键字
typescript
// ❌ Error with noImplicitOverride
class Animal {
speak() { console.log('...'); }
}
class Dog extends Animal {
speak() { console.log('Woof!'); } // Error: missing override
}
// ✅ Fix: Add override keyword
class Dog extends Animal {
override speak() { console.log('Woof!'); }
}typescript
// ❌ 开启noImplicitOverride时会报错
class Animal {
speak() { console.log('...'); }
}
class Dog extends Animal {
speak() { console.log('Woof!'); } // 错误:缺少override关键字
}
// ✅ 修复:添加override关键字
class Dog extends Animal {
override speak() { console.log('Woof!'); }
}Best Practices
最佳实践
- Start strict - Enable strict mode from day one
- No - Use
any+ type guards insteadunknown - Validate boundaries - Use Zod for external data (API, forms)
- Branded types - Prevent ID mixups in domain logic
- Result types - Make error handling explicit
- 从严格模式开始 - 从项目第一天就启用严格模式
- 禁用- 改用
any+ 类型守卫unknown - 校验边界数据 - 对外部数据(API、表单)使用Zod
- 使用Branded类型 - 在领域逻辑中避免ID混淆
- 使用Result类型 - 让错误处理更明确
Common Mistakes
常见错误
- Using to silence errors (use
anyinstead)unknown - Ignoring warnings
noUncheckedIndexedAccess - Not validating data at system boundaries
- Mixing up IDs without branded types
- Using non-null assertion without certainty
!
- 使用来掩盖错误(改用
any)unknown - 忽略警告
noUncheckedIndexedAccess - 在校验系统边界处的数据
- 未使用Branded类型导致ID混淆
- 在不确定的情况下使用非空断言
!
Related Skills
相关技能
- Environment Config
- Request Validation
- Error Handling
- 环境配置
- 请求校验
- 错误处理