TS学习笔记(入门级别推荐)
TS学习笔记(入门级别推荐)
一、背景与问题
TypeScript 是 Microsoft 在 2012 年推出的开源编程语言,它在 JavaScript 基础上添加了静态类型系统。对于现代前端开发来说,TypeScript 已经成为主流选择之一,其核心价值在于:
- 提供编译时类型检查
- 支持面向对象编程
- 提供更强大的 IDE 支持
- 支持装饰器等高级语法
在实际开发中,TypeScript 能帮助开发者提前发现 80% 的语法错误,但其核心原理和使用方式仍存在诸多值得深入理解的细节。
二、基本原理
TypeScript 的核心原理是通过类型注解和类型推断机制,将 JavaScript 转换为可执行的 JavaScript 代码。其核心处理流程如下:
- 语法解析:将源代码解析为抽象语法树(AST)
- 类型检查:根据类型注解进行类型校验
- 代码转换:将类型信息移除,生成纯 JavaScript 代码
- 代码优化:可选的代码压缩和优化
TypeScript 的类型系统分为 10 大基础类型,包括 string、number、boolean、void 等,以及更复杂的联合类型、交叉类型等。其类型检查机制在编译时完成,不会影响运行时性能。
三、环境准备
推荐使用 VS Code 作为开发工具,配合以下配置:
# 安装 TypeScript
npm install -g typescript
# 初始化项目
tsc --init在 tsconfig.json 中配置:
{
"compilerOptions": {
"target": "ES6",
"module": "ESNext",
"strict": true,
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "./dist"
},
"include": ["src/**/*"]
}四、核心实现
1. 类型标注与类型推断
TypeScript 的类型标注可以显式声明变量类型,也可以通过上下文推断类型:
// 显式类型标注
let name: string = 'Alice';
let age: number = 30;
// 类型推断
let greeting = 'Hello, TypeScript!'; // 推断为 string 类型关键点说明:
strict模式会强制类型检查- 类型推断能减少冗余的类型标注
- 类型断言(
as)用于覆盖类型推断结果
2. 接口与类型别名
接口用于定义对象的结构,类型别名用于创建复杂类型:
interface User {
id: number;
name: string;
email?: string; // 可选属性
}
type UserResponse = {
data: User;
status: number;
};
function getUser(id: number): UserResponse {
return {
data: { id, name: 'Bob' },
status: 200
};
}关键点说明:
- 接口可以被实现(
implements) - 类型别名可以包含函数、联合类型等
?表示可选属性
3. 类与装饰器
TypeScript 的类系统支持继承、构造函数、静态属性等:
class Person {
constructor(public name: string) {}
greet(): void {
console.log(`Hello, ${this.name}`);
}
}
class Student extends Person {
constructor(name: string, public grade: number) {
super(name);
}
}装饰器是通过 @ 符号添加的特殊注解,用于修改类或方法的行为:
function log(target: Function) {
target.prototype.log = function() {
console.log('Logging...');
};
}
@log
class Logger {
log() {
console.log('Logging...');
}
}关键点说明:
- 装饰器需要在
tsconfig.json中启用experimentalDecorators选项 - 装饰器可以修改类的原型链
- 装饰器的执行顺序遵循特定规则
五、完整案例
待办事项管理器(Todo Manager)
构建一个完整的待办事项管理器,包含添加、删除、标记完成等操作。
项目结构
todo-app/
├── src/
│ ├── models/
│ │ └── Todo.ts
│ ├── services/
│ │ └── TodoService.ts
│ ├── controllers/
│ │ └── TodoController.ts
│ └── main.ts
├── tsconfig.json
└── package.jsonTodo.ts
export interface Todo {
id: number;
title: string;
completed: boolean;
createdAt: Date;
}TodoService.ts
import { Todo } from './models/Todo';
class TodoService {
private todos: Todo[] = [];
addTodo(title: string): Todo {
const todo: Todo = {
id: Date.now(),
title,
completed: false,
createdAt: new Date()
};
this.todos.push(todo);
return todo;
}
getTodos(): Todo[] {
return this.todos;
}
markAsCompleted(id: number): void {
const todo = this.todos.find(todo => todo.id === id);
if (todo) {
todo.completed = true;
}
}
deleteTodo(id: number): void {
this.todos = this.todos.filter(todo => todo.id !== id);
}
}
export default new TodoService();TodoController.ts
import TodoService from './services/TodoService';
export class TodoController {
private service: TodoService;
constructor() {
this.service = TodoService;
}
addTodo(title: string) {
return this.service.addTodo(title);
}
getTodos() {
return this.service.getTodos();
}
markAsCompleted(id: number) {
this.service.markAsCompleted(id);
}
deleteTodo(id: number) {
this.service.deleteTodo(id);
}
}main.ts
import { TodoController } from './controllers/TodoController';
const controller = new TodoController();
// 添加待办事项
const todo1 = controller.addTodo('学习 TypeScript');
const todo2 = controller.addTodo('编写博客');
console.log('创建的待办事项:', controller.getTodos());
// 标记完成
controller.markAsCompleted(todo1.id);
// 删除待办事项
controller.deleteTodo(todo2.id);
console.log('最终的待办事项:', controller.getTodos());运行结果:
创建的待办事项: [
{ id: 1633603200, title: '学习 TypeScript', completed: false, createdAt: ... },
{ id: 1633603201, title: '编写博客', completed: false, createdAt: ... }
]
最终的待办事项: [
{ id: 1633603200, title: '学习 TypeScript', completed: true, createdAt: ... }
]六、源码解析
以 TodoService 类为例,其核心逻辑如下:
addTodo方法创建新的 Todo 对象,使用Date.now()作为唯一 IDgetTodos方法返回当前所有的待办事项markAsCompleted方法通过 ID 找到待办事项并修改状态deleteTodo方法通过 ID 过滤掉待办事项
关键点说明:
- 使用了类型断言确保方法参数类型
- 通过接口定义了数据结构
- 采用了模块化设计,符合单一职责原则
七、进阶使用
1. 联合类型与类型守卫
type Animal = 'dog' | 'cat' | 'bird';
function describe(animal: Animal): string {
switch (animal) {
case 'dog':
return 'A dog is a loyal companion.';
case 'cat':
return 'A cat is a independent animal.';
case 'bird':
return 'A bird can fly.';
default:
return 'Unknown animal';
}
}关键点说明:
- 联合类型允许变量拥有多种类型
- 类型守卫(
typeof、instanceof等)用于缩小类型范围
2. 泛型与类型参数
function identity<T>(arg: T): T {
return arg;
}
console.log(identity<string>('TypeScript'));
console.log(identity<number>(42));关键点说明:
- 泛型允许函数处理多种类型
- 类型参数可以是类、接口、函数等
- 可以通过约束(
extends)限制泛型类型
3. 装饰器应用
function log(target: Function) {
target.prototype.log = function() {
console.log('Logging...');
};
}
@log
class Logger {
log() {
console.log('Logging...');
}
}
const logger = new Logger();
logger.log(); // 输出 "Logging..."关键点说明:
- 装饰器可以修改类的原型链
- 装饰器可以用于日志记录、权限控制等场景
- 装饰器的执行顺序遵循特定规则
八、性能与工程实践
1. 性能优化
- 使用
--noEmit选项避免生成多余文件 - 使用
--build模式进行批量编译 - 对大型项目使用
tsconfig.json的include和exclude优化编译范围
2. 异常处理
try {
const todo = controller.getTodos().find(todo => todo.id === 123);
if (!todo) {
throw new Error('Todo not found');
}
console.log(todo);
} catch (error) {
console.error('Error:', error.message);
}关键点说明:
- 异常处理需要考虑所有可能的错误情况
- 使用
try/catch捕获运行时错误 - 对于类型错误,可以使用类型守卫进行处理
3. 安全风险
TypeScript 的类型系统可以防止许多安全漏洞,但需要注意:
- 类型断言可能导致类型错误
- 装饰器可能引入运行时风险
- 联合类型可能导致意外的类型转换
九、常见问题与踩坑
1. 类型断言错误
const value = '123' as number; // 错误:字符串不能强制转换为数字解决办法:
- 使用
Number()函数进行显式转换 - 使用类型守卫进行类型检查
2. 装饰器顺序问题
@A
@B
class C {}问题:
- 装饰器的执行顺序是
B然后A,而不是A然后B
解决办法:
- 使用
@applyDecorators等工具函数控制顺序
3. 类型推断错误
function add(a: number, b: number) {
return a + b;
}
console.log(add(1, '2')); // 编译错误:类型不匹配解决办法:
- 显式标注类型
- 使用类型断言
- 启用
strict模式
十、最佳实践
类型标注规范
- 对所有变量进行类型标注
- 使用接口定义复杂类型
- 对函数参数和返回值进行类型标注
模块化设计
- 使用
models、services、controllers等目录组织代码 - 遵循单一职责原则
- 使用
装饰器使用规范
- 只在需要时使用装饰器
- 避免过度使用装饰器导致代码难以维护
性能优化
- 使用
--noEmit避免生成多余文件 - 对大型项目使用
tsconfig.json的include和exclude优化编译范围
- 使用
安全实践
- 对用户输入进行类型检查
- 使用类型断言时要谨慎
- 对敏感数据进行加密处理
十一、总结
TypeScript 作为 JavaScript 的超集,通过引入静态类型系统,显著提升了代码质量和开发效率。在实际项目中,我们应:
- 在大型项目中使用 TypeScript 提升可维护性
- 在团队协作中使用 TypeScript 确保代码一致性
- 在需要类型检查的场景中使用 TypeScript
同时也要注意:
- 避免在简单脚本中过度使用 TypeScript
- 避免对动态类型进行过度限制
- 注意装饰器和类型断言的使用风险
通过合理使用 TypeScript,可以显著提升代码质量和开发效率,但需要根据具体项目需求选择合适的使用方式。
评论已关闭