Appearance
TS 新手常见错误与避坑
类型不匹配
常见场景
- 变量类型与赋值类型不一致
- 函数参数类型与调用时传入类型不匹配
- 函数返回值类型与实际返回值不匹配
错误示例
typescript
// 错误:类型不匹配
let age: number = "18"; // 类型 'string' 不能赋值给类型 'number'
// 错误:函数参数类型不匹配
function greet(name: string) {
console.log(`Hello, ${name}!`);
}
greet(18); // 类型 'number' 不能赋值给类型 'string'
// 错误:函数返回值类型不匹配
function add(a: number, b: number): number {
return "result" + (a + b); // 类型 'string' 不能赋值给类型 'number'
}解决方案
- 明确指定变量类型
- 检查函数参数和返回值类型
- 使用类型断言进行类型转换(谨慎使用)
- 利用 TypeScript 的类型推断
对象缺少属性
常见场景
- 创建对象时缺少必填属性
- 访问对象不存在的属性
- 函数参数对象缺少必要属性
错误示例
typescript
interface User {
name: string;
age: number;
email: string;
}
// 错误:缺少必填属性
const user: User = {
name: "John"
// 缺少 age 和 email
};
// 错误:访问不存在的属性
const user: User = {
name: "John",
age: 30,
email: "john@example.com"
};
console.log(user.phone); // 属性 'phone' 不存在于类型 'User' 上解决方案
- 使用可选属性(
?) - 使用默认值
- 使用类型守卫检查属性存在性
- 确保对象结构与接口定义一致
函数参数错误
常见场景
- 函数参数数量不匹配
- 可选参数位置错误
- 剩余参数使用错误
错误示例
typescript
// 错误:参数数量不匹配
function add(a: number, b: number): number {
return a + b;
}
add(1, 2, 3); // 应有 2 个参数,但获得 3 个
// 错误:可选参数位置错误
function greet(name: string, age?: number, address: string): string {
return `Hello, ${name}!`;
}
// 可选参数必须放在必选参数之后
// 错误:剩余参数使用错误
function sum(...numbers: number): number {
return numbers.reduce((a, b) => a + b, 0);
}
// 剩余参数必须是数组类型解决方案
- 确保参数数量与函数定义一致
- 可选参数放在必选参数之后
- 剩余参数使用数组类型
- 使用默认参数值
无法推断类型
常见场景
- 复杂对象类型推断失败
- 泛型类型推断失败
- 联合类型推断不明确
错误示例
typescript
// 错误:复杂对象类型推断失败
const user = {
name: "John",
age: 30,
address: {
street: "123 Main St",
city: "New York"
}
};
// TypeScript 可以推断基本类型,但复杂嵌套对象可能需要显式类型
// 错误:泛型类型推断失败
function identity<T>(arg: T): T {
return arg;
}
const result = identity({ name: "John" });
// 类型被推断为 { name: string },但可能需要更精确的类型
// 错误:联合类型推断不明确
let value: string | number;
value = "hello";
value.toUpperCase(); // 类型 'string | number' 上不存在属性 'toUpperCase'解决方案
- 显式指定类型注解
- 使用类型断言
- 使用类型守卫
- 为复杂对象定义接口
any 滥用问题
常见场景
- 随意使用 any 类型
- 禁用 TypeScript 的类型检查
- 失去 TypeScript 的类型安全优势
错误示例
typescript
// 错误:滥用 any
let data: any = fetch("https://api.example.com/data");
data = data.json();
data.users.forEach(user => console.log(user.name));
// 失去类型检查,可能运行时错误
// 错误:函数返回 any
function getData(): any {
return { name: "John", age: 30 };
}
const user = getData();
user.address.street; // 运行时错误,address 不存在解决方案
- 尽量避免使用 any
- 使用 unknown 类型代替 any
- 为复杂类型定义接口
- 利用 TypeScript 的类型推断
编译报错排查
常见编译错误
- 类型不匹配
- 缺少属性
- 访问不存在的属性
- 函数参数错误
- 循环依赖
错误信息解读
类型不匹配
Type 'string' is not assignable to type 'number'.- 原因:尝试将字符串赋值给数字类型
- 解决方案:检查类型注解,确保类型一致
缺少属性
Property 'age' is missing in type '{ name: string; }' but required in type 'User'.- 原因:创建对象时缺少必填属性
- 解决方案:添加缺少的属性,或使用可选属性
访问不存在的属性
Property 'phone' does not exist on type 'User'.- 原因:访问对象不存在的属性
- 解决方案:检查属性名,或使用可选链操作符
函数参数错误
Expected 2 arguments, but got 3.- 原因:函数调用时参数数量不匹配
- 解决方案:检查函数定义,确保参数数量一致
循环依赖
Circular dependency detected.- 原因:模块之间相互引用形成循环
- 解决方案:重构代码,消除循环依赖
排查技巧
- 仔细阅读错误信息
- 检查类型注解和接口定义
- 使用 TypeScript 编译器的错误定位
- 利用 IDE 的类型提示和错误检查
- 逐步排查,从简单到复杂
最佳实践
使用严格模式
- 在 tsconfig.json 中启用 strict: true
- 开启所有严格类型检查选项
合理使用类型
- 为所有变量和函数添加类型注解
- 使用接口定义复杂数据结构
- 利用泛型提高代码复用性
避免 any 类型
- 使用 unknown 类型代替 any
- 为第三方库添加类型定义
- 逐步消除代码中的 any
使用类型守卫
- 对联合类型使用类型守卫
- 确保类型安全的类型转换
代码组织
- 按功能模块化代码
- 合理使用命名空间和模块
- 保持类型定义的一致性
