前言
TypeScript 在前端项目中已经是标配,使用 TypeScript 有很多好处,它让 JavaScript 拥有了强大的类型系统。
由于 JavaScript 本身是弱类型,很多前端开发人员并没有类型概念,对类型系统理解不深,常常走了不少弯路。本文总结了 16 个最常见的坑,结合实际例子帮助你少踩坑、写出更安全、更可维护的代码。
滥用 any
有些小伙伴在做 TS 项目时,为了避免写类型,直接使用any
。any
会关闭类型检查,相当于"告诉 TypeScript 别管我了"。这会让整个类型系统形同虚设。
错误示例:
typescript
function parseData(data: any) {
return data.user.name.toUpperCase();
}
parseData(null); // 运行时报错!
正确示例:
typescript
interface User {
name: string;
}
interface Data {
user: User;
}
function parseData(data: Data): string {
return data.user.name.toUpperCase();
}
尽量避免使用 any
,使用 unknown
或明确类型更安全。满屏幕使用any
是专业性不足的表现
不写函数返回值类型
TypeScript 会自动推导返回值,但在复杂逻辑中,显式声明返回类型可以增强可读性和类型保护。笔者认为最重要的是为了维护
错误示例:
typescript
function getUser(id: number) {
if (id === 1) return 'admin';
return null;
}
正确示例:
typescript
function getUser(id: number): string | null {
if (id === 1) return 'admin';
return null;
}
显式声明返回类型是良好的代码习惯,尤其适用于公共函数 和库函数。
Interface 和 Type 定义太随意 / 不抽离
Interface 和 Type 是 TS 中非常重要的两种类型,在使用他们的时候,命名非常非常重要。写临时类型难以复用和维护
错误示例:
typescript
function login(user: { username: string; password: string }) {
// ...
}
正确示例:
typescript
interface LoginParams {
username: string;
password: string;
}
function login(user: LoginParams) {
// ...
}
类型应当抽离,复用更方便,代码更清晰。
在对象或函数参数中直接使用类型断言
类型断言(Type Assertion)是 TypeScript 中的一种语法,表示你明确告诉编译器某个值的类型是什么,即使它当前的推导类型不是你说的这个。
它不会做任何实际的运行时转换,仅仅用于绕过编译器的类型检查,常见于你"确信"某个值是什么类型但 TypeScript 推断不出来时。
只有在特殊场景下才使用类型断言,类型断言会破坏类型自动推导
错误示例:
typescript
const data = fetchData() as any;
const name = (data as { user: { name: string } }).user.name;
正确示例:
typescript
interface UserData {
user: {
name: string;
};
}
const data: UserData = fetchData();
const name = data.user.name;
类型断言不是万能钥匙,优先使用类型声明、接口、泛型。
不使用工具类型
TypeScript 提供了很多内置工具类型(如 Partial
, Pick
, Omit
等),可以简化代码并增强复用性。
错误示例:
typescript
interface User {
id: number;
name: string;
age: number;
}
type UserPreview = {
id: number;
name: string;
};
正确示例:
typescript
type UserPreview = Pick<User, 'id' | 'name'>;
合理使用工具类型,写出更简洁更灵活的代码。
混用 interface
和 type
不明确
两者功能类似,但存在一些差异,新手容易随意混用。
建议:
- 用
interface
定义对象结构。 - 用
type
做联合类型、工具类型等更灵活的组合。
错误示例:
typescript
type User = {
name: string;
};
interface User {
age: number;
} // 冲突!
正确示例:
typescript
interface User {
name: string;
age: number;
}
小结:
interface
用于结构定义,type
用于类型组合和增强。
不使用枚举(Enum)管理常量值
魔法字符串易出错、难维护,应该用枚举统一管理。
错误示例:
typescript
function getRole(role: string) {
if (role === 'admin') return '管理员';
}
正确示例:
typescript
enum Role {
Admin = 'admin',
User = 'user',
}
function getRole(role: Role) {
if (role === Role.Admin) return '管理员';
}
常量值统一管理,提高可维护性。
类型不匹配却强行通过断言
使用 as
并不会真正转换类型,只是"告诉 TypeScript 你信我"。
错误示例:
typescript
const val = '123' as unknown as number;
正确示例:
typescript
const val = Number('123'); // 类型安全的转换
类型转换要谨慎,as
不能解决逻辑错误。
类型重复,没用泛型抽象
多个函数或接口出现重复代码时,应抽象为泛型。
错误示例:
typescript
function wrapString(value: string): string[] {
return [value];
}
function wrapNumber(value: number): number[] {
return [value];
}
正确示例:
typescript
function wrap<T>(value: T): T[] {
return [value];
}
泛型能让你的类型更具扩展性和复用性。
不启用 strict
模式
strict
是 TypeScript 中最核心的类型检查开关。关闭它会让很多问题"躲"过编译器。
正确做法:
在 tsconfig.json
中启用:
json
{
"compilerOptions": {
"strict": true
}
}
开启 strict
模式,是 TypeScript 真正发挥威力的前提。
忽视 IDE 的提示与错误
VSCode 等 IDE 通常会高亮潜在的类型问题,但很多新手视而不见。
错误示例:
typescript
const name: string = 123; // TS 提示类型错误,但被忽略
编译器和 IDE 是你最好的朋友,请认真对待他们的提示。
忽视类型收窄(type narrowing)
类型收窄是 TypeScript 根据条件判断自动"缩小"变量的类型。
错误示例:
typescript
function printLength(str: string | null) {
return str.length; // 报错
}
正确示例:
typescript
function printLength(str: string | null) {
if (str) {
return str.length;
}
return 0;
}
利用 typeof
, in
, instanceof
等收窄类型,避免错误。
忽略 undefined
和 null
的存在
TypeScript 默认不强制处理 null
和 undefined
,需要手动开启 strictNullChecks
。
错误示例:
typescript
function greet(name: string) {
return 'Hello ' + name.toUpperCase(); // name 可能为 undefined
}
正确示例:
typescript
function greet(name?: string) {
return name ? 'Hello ' + name.toUpperCase() : 'Hello';
}
尽量避免写出不处理空值的代码,空值检查是常识。
忘记在对象访问前做空值判断
对象嵌套访问时,未做空值判断极易导致运行时错误。
错误示例:
typescript
const username = user.profile.name;
正确示例:
typescript
const username = user?.profile?.name ?? 'Anonymous';
善用可选链(?.
)和空值合并运算符(??
)提高代码健壮性。
使用泛型却不指定类型参数
TypeScript 中的泛型应显式传入类型,避免类型不明确或不被推导。
错误示例:
typescript
const arr = Array(); // 类型为 any[]
正确示例:
typescript
const arr: Array<number> = [];
显式指定泛型参数有助于代码的可读性和类型安全。
混用 ==
和 ===
==
会进行隐式类型转换,容易出现意料之外的结果。
错误示例:
typescript
if (value == null) {
// 可能同时为 undefined 或 null
}
正确示例:
typescript
if (value === null) {
// 明确判断
}
在 TypeScript 中应始终使用 ===
和 !==
,杜绝隐式类型转换。
总结
写好 TypeScript 其实并不难,关键在于理解类型系统的设计意图,并养成良好的编码习惯