【百度网盘】求知久久-诱人的 TypeScript 视频教程

6 阅读4分钟

诱人的 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. 基础阶段(1-2周)

    • 类型注解、接口、类
    • 函数类型、泛型基础
    • 类型推断和类型兼容性
  2. 进阶阶段(2-3周)

    • 高级类型:联合类型、交叉类型、映射类型
    • 类型守卫、类型断言
    • 模块和命名空间
  3. 高级阶段(3-4周)

    • 装饰器、元编程
    • 条件类型、模板字面量类型
    • 声明文件和类型定义
  4. 实战阶段(持续)

    • 框架集成(React、Vue、Angular)
    • Node.js 后端开发
    • 工程化配置和优化

总结

TypeScript 的魅力在于:

  1. 开发时安全:在编码阶段捕获潜在错误
  2. 智能提示:强大的 IDE 支持和自动完成
  3. 代码可维护性:清晰的类型定义作为文档
  4. 渐进式采用:可以逐步将 JavaScript 项目迁移到 TypeScript

通过系统学习 TypeScript,开发者不仅能够写出更健壮的代码,还能享受到更愉悦的开发体验。无论是前端还是后端开发,TypeScript 都已成为现代 Web 开发的重要技能。