TypeScript strict 模式下的类型技巧
TypeScript
开启 strict: true 后遇到的常见问题和解法,包括 null 检查、泛型约束、条件类型等实用模式。
什么是 strict 模式?
在 tsconfig.json 中设置 "strict": true 会启用一系列严格的类型检查选项:
{
"compilerOptions": {
"strict": true,
// 等同于同时启用:
// "noImplicitAny": true,
// "noImplicitThis": true,
// "strictNullChecks": true,
// "strictFunctionTypes": true,
// "strictBindCallApply": true,
// "strictPropertyInitialization": true,
// "noImplicitReturns": true,
// "alwaysStrict": true
}
}
这确保代码的类型安全性更高,但也会产生一些新的挑战。
常见问题与解法
1. Null/Undefined 检查
最常见的问题是处理可能为 null 的值:
// ❌ strict 模式下会报错
function greet(name: string) {
console.log(name.toUpperCase());
}
greet(null); // Error!
// ✓ 正确做法
function greet(name: string | null) {
if (name) {
console.log(name.toUpperCase());
}
}
// ✓ 使用非空断言(谨慎使用)
function greet(name: string | null) {
console.log(name!.toUpperCase());
}
2. 可选链和 Nullish Coalescing
// ✓ 可选链操作符 ?.
const user = data?.user?.name;
// ✓ Nullish Coalescing ??
const count = data?.count ?? 0;
// ✓ 组合使用
const greeting = user?.name?.toUpperCase() ?? 'Guest';
3. 泛型约束
// ❌ 太宽泛
function getValue<T>(obj: T, key: string) {
return obj[key]; // Error: string 不能作为 T 的索引
}
// ✓ 使用 keyof 约束
function getValue<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const user = { name: 'Jerry', age: 25 };
const name = getValue(user, 'name'); // ✓ 类型正确推导为 string
const age = getValue(user, 'age'); // ✓ 类型正确推导为 number
// getValue(user, 'email'); // Error!
4. 条件类型
// ✓ 根据输入类型返回不同类型
type Flatten<T> = T extends Array<infer U> ? U : T;
type A = Flatten<string[]>; // string
type B = Flatten<string>; // string
// ✓ 实际应用:API 响应处理
type APIResponse<T> = T extends { data: infer D }
? D
: T;
interface UserResponse {
data: { id: number; name: string };
}
type UserData = APIResponse<UserResponse>;
// { id: number; name: string }
5. readonly 修饰符
// ✓ 防止意外修改
interface User {
readonly id: number;
readonly name: string;
age: number; // 可修改
}
const user: User = { id: 1, name: 'Jerry', age: 25 };
user.age = 26; // ✓ OK
user.name = 'Li'; // ❌ Error!
// ✓ 数组的 readonly
function processArray(arr: readonly string[]) {
arr[0] = 'test'; // ❌ Error!
arr.push('test'); // ❌ Error!
}
6. 避免 any 的模式
// ❌ 坏习惯
function process(data: any) {
return data.something.nested.value;
}
// ✓ 使用 unknown 和类型守卫
function process(data: unknown) {
if (typeof data === 'object' && data !== null && 'value' in data) {
return (data as { value: string }).value;
}
}
// ✓ 使用类型谓词
function isUser(obj: unknown): obj is User {
return (
typeof obj === 'object' &&
obj !== null &&
'id' in obj &&
'name' in obj
);
}
if (isUser(data)) {
console.log(data.name); // ✓ 类型已缩小为 User
}
最佳实践
- 使用可选链和 Nullish Coalescing:减少冗长的 if 判断
- 充分利用泛型:写出可复用的类型安全代码
- 避免 any:如果确实无法推导,用
unknown+ 类型守卫 - 使用类型谓词:自定义类型缩小逻辑
- 定义清晰的接口:在函数参数和返回值上标注类型
strict 模式刚开始会让你觉得烦人,但一旦习惯了,你会发现它能提前捕获很多 bug,让代码更安全、更易维护。