诱人的 TypeScript 视频教程:从 JavaScript 到类型安全的优雅蜕变
TypeScript 作为 JavaScript 的超集,正在成为现代前端开发的标配。它不仅提供了强大的类型系统,还带来了更好的开发体验和代码维护性。本文将带你领略 TypeScript 的魅力所在。
TypeScript 的核心优势
类型安全:开发阶段的错误拦截
// 基础类型注解
interface User {
id: number;
name: string;
email: string;
age?: number; // 可选属性
readonly createdAt: Date; // 只读属性
}
// 类型安全的函数定义
function createUser(user: User): User {
// TypeScript 会在编译时检查参数类型
if (!user.email.includes('@')) {
throw new Error('Invalid email address');
}
return {
...user,
createdAt: new Date()
};
}
// 使用泛型增强复用性
class Repository<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
findById(id: number): T | undefined {
return this.items.find(item => (item as any).id === id);
}
getAll(): T[] {
return [...this.items];
}
}
// 使用示例
const userRepo = new Repository<User>();
const newUser: User = {
id: 1,
name: 'Alice',
email: 'alice@example.com',
createdAt: new Date()
};
userRepo.add(newUser);
console.log(userRepo.findById(1));
高级类型特性
联合类型与类型守卫
// 联合类型和类型收缩
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'rectangle'; width: number; height: number }
| { kind: 'triangle'; base: number; height: number };
// 类型守卫函数
function isCircle(shape: Shape): shape is { kind: 'circle'; radius: number } {
return shape.kind === 'circle';
}
function calculateArea(shape: Shape): number {
// 基于类型守卫的智能类型推断
if (isCircle(shape)) {
// 这里 TypeScript 知道 shape 是圆形
return Math.PI * shape.radius ** 2;
}
switch (shape.kind) {
case 'rectangle':
return shape.width * shape.height;
case 'triangle':
return (shape.base * shape.height) / 2;
default:
// 穷尽性检查:确保处理了所有情况
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}
// 映射类型和条件类型
type Optional<T> = {
[P in keyof T]?: T[P];
};
type ReadonlyUser = Readonly<User>;
type PartialUser = Partial<User>;
type PickUser = Pick<User, 'name' | 'email'>;
// 条件类型
type NonNullable<T> = T extends null | undefined ? never : T;
type UserName = NonNullable<User['name']>;
装饰器与元编程
// 类装饰器
function Controller(prefix: string) {
return function<T extends { new (...args: any[]): {} }>(constructor: T) {
return class extends constructor {
routePrefix = prefix;
createdAt = new Date();
};
};
}
// 方法装饰器
function LogExecution(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const method = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`调用方法: ${propertyName}`, args);
const result = method.apply(this, args);
console.log(`方法结果: ${propertyName}`, result);
return result;
};
}
// 属性装饰器
function DefaultValue(value: any) {
return function (target: any, propertyName: string) {
target[propertyName] = value;
};
}
@Controller('/api/users')
class UserController {
@DefaultValue('anonymous')
private defaultName: string;
@LogExecution
createUser(userData: Partial<User>): User {
return {
id: Math.random(),
name: userData.name || this.defaultName,
email: userData.email || '',
createdAt: new Date()
};
}
@LogExecution
getUser(id: number): User | null {
// 模拟数据获取
return id === 1 ? {
id: 1,
name: 'Test User',
email: 'test@example.com',
createdAt: new Date()
} : null;
}
}
现代前端框架集成
React + TypeScript 完美组合
import React, { useState, useEffect, useCallback } from 'react';
// 定义组件 Props 类型
interface UserCardProps {
user: User;
onEdit?: (user: User) => void;
onDelete?: (userId: number) => void;
isAdmin?: boolean;
}
// 定义组件状态类型
interface UserCardState {
isEditing: boolean;
isLoading: boolean;
error: string | null;
}
// 使用泛型约束的 React 组件
const UserCard: React.FC<UserCardProps> = ({
user,
onEdit,
onDelete,
isAdmin = false
}) => {
const [state, setState] = useState<UserCardState>({
isEditing: false,
isLoading: false,
error: null
});
// 类型安全的回调函数
const handleEdit = useCallback(async () => {
setState(prev => ({ ...prev, isLoading: true, error: null }));
try {
onEdit?.(user);
setState(prev => ({ ...prev, isEditing: true, isLoading: false }));
} catch (error) {
setState(prev => ({
...prev,
error: error instanceof Error ? error.message : '编辑失败',
isLoading: false
}));
}
}, [user, onEdit]);
const handleDelete = useCallback(async () => {
if (!window.confirm('确定要删除这个用户吗?')) {
return;
}
setState(prev => ({ ...prev, isLoading: true }));
try {
onDelete?.(user.id);
} catch (error) {
setState(prev => ({
...prev,
error: error instanceof Error ? error.message : '删除失败'
}));
} finally {
setState(prev => ({ ...prev, isLoading: false }));
}
}, [user.id, onDelete]);
return (
<div className="user-card">
<h3>{user.name}</h3>
<p>邮箱: {user.email}</p>
{user.age && <p>年龄: {user.age}</p>}
<div className="actions">
<button onClick={handleEdit} disabled={state.isLoading}>
{state.isLoading ? '编辑中...' : '编辑'}
</button>
{isAdmin && (
<button onClick={handleDelete} disabled={state.isLoading}>
{state.isLoading ? '删除中...' : '删除'}
</button>
)}
</div>
{state.error && <div className="error">{state.error}</div>}
</div>
);
};
// 使用泛型约束的自定义 Hook
function useApi<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json() as T;
setData(result);
} catch (err) {
setError(err instanceof Error ? err.message : '未知错误');
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
// 使用自定义 Hook
const UserList: React.FC = () => {
const { data: users, loading, error } = useApi<User[]>('/api/users');
if (loading) return <div>加载中...</div>;
if (error) return <div>错误: {error}</div>;
if (!users) return <div>暂无数据</div>;
return (
<div className="user-list">
{users.map(user => (
<UserCard
key={user.id}
user={user}
isAdmin={true}
/>
))}
</div>
);
};
Node.js 后端开发
Express + TypeScript 服务端开发
import express, { Request, Response, NextFunction } from 'express';
import { ParamsDictionary } from 'express-serve-static-core';
// 扩展 Express Request 类型
interface AuthenticatedRequest extends Request {
user?: User;
}
// 自定义错误类型
class AppError extends Error {
constructor(
public statusCode: number,
public message: string,
public isOperational = true
) {
super(message);
Object.setPrototypeOf(this, AppError.prototype);
}
}
// 类型安全的中间件
const authMiddleware = (
req: AuthenticatedRequest,
res: Response,
next: NextFunction
) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
throw new AppError(401, '未提供认证令牌');
}
// 模拟用户验证
try {
req.user = {
id: 1,
name: 'Authenticated User',
email: 'user@example.com',
createdAt: new Date()
};
next();
} catch (error) {
throw new AppError(401, '无效的认证令牌');
}
};
// 类型安全的路由处理器
class UserController {
private userRepository = new Repository<User>();
getUsers = async (req: AuthenticatedRequest, res: Response): Promise<void> => {
const users = this.userRepository.getAll();
res.json({
success: true,
data: users,
count: users.length
});
};
getUserById = async (
req: Request<ParamsDictionary, any, any, { include?: string }>,
res: Response
): Promise<void> => {
const userId = parseInt(req.params.id);
if (isNaN(userId)) {
throw new AppError(400, '无效的用户ID');
}
const user = this.userRepository.findById(userId);
if (!user) {
throw new AppError(404, '用户不存在');
}
res.json({
success: true,
data: user
});
};
createUser = async (req: Request, res: Response): Promise<void> => {
const userData: Omit<User, 'id' | 'createdAt'> = req.body;
// 数据验证
if (!userData.name || !userData.email) {
throw new AppError(400, '姓名和邮箱是必填项');
}
const newUser: User = {
...userData,
id: Date.now(),
createdAt: new Date()
};
this.userRepository.add(newUser);
res.status(201).json({
success: true,
data: newUser
});
};
}
// 错误处理中间件
const errorHandler = (
error: Error,
req: Request,
res: Response,
next: NextFunction
) => {
if (error instanceof AppError) {
res.status(error.statusCode).json({
success: false,
error: error.message
});
} else {
// 未知错误
console.error('未知错误:', error);
res.status(500).json({
success: false,
error: '内部服务器错误'
});
}
};
// 应用初始化
const app = express();
const userController = new UserController();
app.use(express.json());
// 路由定义
app.get('/api/users', authMiddleware, userController.getUsers);
app.get('/api/users/:id', userController.getUserById);
app.post('/api/users', userController.createUser);
app.use(errorHandler);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器运行在端口 ${PORT}`);
});
工程化配置
现代化的 TypeScript 配置
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* 模块解析 */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
/* 类型检查 */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
/* 装饰器支持 */
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
/* 其他 */
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
/* 路径映射 */
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@/components/*": ["src/components/*"],
"@/utils/*": ["src/utils/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
学习路径建议
渐进式掌握 TypeScript
-
基础阶段(1-2周)
- 类型注解、接口、类
- 函数类型、泛型基础
- 类型推断和类型兼容性
-
进阶阶段(2-3周)
- 高级类型:联合类型、交叉类型、映射类型
- 类型守卫、类型断言
- 模块和命名空间
-
高级阶段(3-4周)
- 装饰器、元编程
- 条件类型、模板字面量类型
- 声明文件和类型定义
-
实战阶段(持续)
- 框架集成(React、Vue、Angular)
- Node.js 后端开发
- 工程化配置和优化
总结
TypeScript 的魅力在于:
- 开发时安全:在编码阶段捕获潜在错误
- 智能提示:强大的 IDE 支持和自动完成
- 代码可维护性:清晰的类型定义作为文档
- 渐进式采用:可以逐步将 JavaScript 项目迁移到 TypeScript
通过系统学习 TypeScript,开发者不仅能够写出更健壮的代码,还能享受到更愉悦的开发体验。无论是前端还是后端开发,TypeScript 都已成为现代 Web 开发的重要技能。