Corey Rosamond · 2026/2/15 · 约 5 分钟阅读
本文为英文原文的简体中文译文。资料和事件日期保留原文发表时的语境;代码示例保持原样。 Read in English

为什么使用严格模式
Rosecraft Studios 的 TypeScript 项目以 strict: true 为起点。多年软件工作让我们体会到,在编译阶段发现错误,通常比凌晨两点在生产环境发现更容易处理。
严格模式启用一组编译器检查,可以在执行前发现部分错误。但静态类型不会验证网络响应,也无法消除所有运行时错误。
严格模式究竟启用了什么
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true
}
}
strict 启用一组检查,包括下面这些示例。上方的 noUncheckedIndexedAccess 和未使用代码检查是独立选项,并不由 strict 自动开启。完整列表请查看对应编译器版本的 strict 参考文档:
strictNullChecks:区分空值,要求类型和处理方式与可能为空的情况一致。strictFunctionTypes:更严格地检查函数参数类型兼容性。strictBindCallApply:检查bind、call和apply的类型。strictPropertyInitialization:检查类属性初始化。noImplicitAny:报告无法推断而隐式成为any的情况。noImplicitThis:检查隐式any类型的this。alwaysStrict:按严格模式解析并输出相应的"use strict"。
严格检查能发现的错误
1. 未处理的空引用
没有 strictNullChecks 时,下面的代码可以编译,却可能在运行时崩溃:
// Without strict: compiles, crashes at runtime
function getUserName(users: User[], id: string) {
const user = users.find((u) => u.id === id);
return user.name; // Runtime error: Cannot read property 'name' of undefined
}
// With strict: compile error forces proper handling
function getUserName(users: User[], id: string) {
const user = users.find((u) => u.id === id);
if (!user) {
throw new Error(`User not found: ${id}`);
}
return user.name; // Safe — TypeScript knows user is not undefined
}
Array.find()返回T | undefined。关闭严格空值检查后,编译器不会要求处理找不到记录的情况。显式检查让您决定应用此时应该如何响应。
2. 索引访问陷阱
即便启用 strictNullChecks,默认数组和对象索引访问仍可能掩盖不存在的值。因此我们额外启用 noUncheckedIndexedAccess:
const colors = ['red', 'green', 'blue'];
// Without noUncheckedIndexedAccess
const first = colors[0]; // Type: string (wrong — could be undefined)
// With noUncheckedIndexedAccess
const first = colors[0]; // Type: string | undefined (correct)
// Forces safe access patterns
if (first) {
console.error(first.toUpperCase()); // Safe
}
3. 隐式 any 隐藏的问题
// Without noImplicitAny: compiles, returns wrong type
function parseConfig(raw) {
// 'raw' is implicitly 'any'
return raw.settings.theme; // No type checking at all
}
// With noImplicitAny: forces explicit typing
function parseConfig(raw: RawConfig): string {
return raw.settings.theme; // Fully type-checked
}
在 unknown 与 any 之间作选择
类型确实不确定时,优先使用 unknown,而不是 any。差别在于使用前是否必须检查:
// 'any' disables ALL type checking
function processData(data: any) {
data.forEach((item) => item.transform()); // No errors, no safety
}
// 'unknown' requires narrowing before use
function processData(data: unknown) {
if (!Array.isArray(data)) {
throw new Error('Expected an array');
}
// TypeScript now knows data is an array
data.forEach((item: unknown) => {
if (isTransformable(item)) {
item.transform(); // Safe — type guard verified the shape
}
});
}
any像是在告诉编译器“不用检查我”,而unknown则是“我现在不知道,但使用前会检查”。后者更明确。
Zod:连接运行时验证与静态类型
在 API 路由、表单处理器和外部 API 响应等系统边界,我们使用 Zod schema 同时定义验证规则与类型:
import { z } from 'zod';
const contactSchema = z.object({
name: z.string().min(2).max(100),
email: z.string().email(),
message: z.string().min(10).max(2000),
});
// Infer the TypeScript type from the Zod schema
type ContactForm = z.infer<typeof contactSchema>;
// API route uses the same schema for validation
export async function POST(request: Request) {
const body = await request.json();
const result = contactSchema.safeParse(body);
if (!result.success) {
return Response.json(
{ error: 'Validation failed', details: result.error.flatten() },
{ status: 400 },
);
}
// result.data is fully typed as ContactForm
await saveSubmission(result.data);
}
这种方式可以减少 TypeScript 接口与运行时验证各自维护、逐渐不一致的问题。
迁移到严格模式
现有代码库尚未开启严格模式时,一次性迁移可能很困难。可以逐步推进:
- 一次启用一个检查。例如先处理
strictNullChecks,再处理noImplicitAny。 - 逐个文件修复。临时使用
// @ts-expect-error时,配上可追踪的说明。 - 不再引入新的 any。新代码使用合适类型。
- 随后加入 noUncheckedIndexedAccess。它可能涉及较多索引访问修改。
从能处理代码库已知风险的检查开始。分别记录编译错误与实际缺陷,不要把错误数量当成“防止了多少百分比生产问题”的测量结果。
将静态检查与运行时检查结合
严格模式让开发中的假设更容易看见。还应结合输入验证、现实测试和运行时错误处理。类型断言、无类型依赖和外部数据,仍可能使编译器看来安全的假设失效。
希望用扎实的工程实践开发 TypeScript 项目?聊聊您的需求,一起把这些实践落实为可靠软件。