- 约1015字
- 技术
- 2026年4月13日
用了TypeScript半年,代码里还是满屏的any?每次调试都发现运行时报错,才后悔当初没好好用类型系统。今天把这5个我天天用的高级类型技巧分享出来,帮你在编译阶段就把问题抓个现行。
1. 联合类型 + 类型守卫:告别不确定的变量
function processData(data: string | number) {
// 错误:data.toUpperCase() 会报错
// 正确:使用类型守卫
if (typeof data === 'string') {
console.log(data.toUpperCase());
} else {
console.log(data.toFixed(2));
}
}
联合类型让一个变量可以是多种类型,但直接使用会有类型错误。类型守卫就是让TypeScript知道“现在是哪种类型”的开关。最常用的是typeof判断,还可以使用in操作符判断对象属性。
2. 泛型约束:让通用函数不再“any到底”
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(item: T): void {
console.log(item.length);
}
// ✅ 正确
logLength("hello");
logLength([1, 2, 3]);
// ❌ 报错:number没有length属性
logLength(123);
泛型让函数可以处理多种类型,但有时我们需要限制范围。extends关键字就是泛型约束,它告诉TypeScript:“只要你有length属性就能进来”。这样既保持了通用性,又避免了类型丢失。
3. Partial 和 Required:动态调整属性必要性
interface User {
name: string;
age: number;
email: string;
}
// 更新用户时,只需要部分字段
type PartialUser = Partial<User>;
// 等同于:{ name?: string; age?: number; email?: string; }
// 创建用户时,所有字段必填
type RequiredUser = Required<User>;
Partial<T>把所有属性变成可选的,Required<T>把所有属性变成必填的。这在更新数据、创建表单等场景特别实用。想想以前你要手写一堆?: string,现在一行搞定。
4. Record:快速构建对象类型
type Role = 'admin' | 'user' | 'guest';
const permissions: Record<Role, string[]> = {
admin: ['read', 'write', 'delete'],
user: ['read', 'write'],
guest: ['read']
};
// 错误:guest被赋值为字符串
// permissions.guest = 'no-access'; // 报错
Record<Keys, Type>创建一个对象类型,Keys是键的类型,Values是值的类型。它比手动定义对象类型更简洁,也更容易保证一致性。特别是当你有一组固定的枚举值需要映射时,特别好用。
5. ReturnType:自动推断函数返回值类型
function fetchUser() {
return { id: 1, name: 'Tom', email: 'tom@example.com' };
}
// 自动推断出 { id: number; name: string; email: string }
type UserResponse = ReturnType<typeof fetchUser>;
// 在另一个函数中使用
function createUserResponse(): UserResponse {
return { id: 2, name: 'Jerry', email: 'jerry@example.com' };
}
ReturnType<T>提取函数的返回值类型。这意味着你不需要手动定义返回类型,TypeScript会自动帮你搞定。当你要基于现有函数构建新类型时,这个技巧能省很多功夫。
以上5个高级类型技巧,是我日常开发中使用频率最高的。它们不复杂,但确实能帮你在编译阶段发现问题,减少运行时报错。
类型系统不是一天学会的,但每天用一点,半年后你会发现:原来代码可以写得这么安全。