项目基础准备之TypeScript 从入门到项目实战:打造类型安全的 React 应用

56 阅读3分钟

TypeScript 从入门到项目实战:打造类型安全的 React 应用

一、为什么需要 TypeScript?

JavaScript 是一门动态类型语言,灵活但容易出错。随着项目规模增大,变量类型混乱、函数参数错误、对象结构不明确等问题频发。TypeScript(简称 TS)作为 JS 的超集,通过静态类型检查在编码阶段就捕获潜在错误,大幅提升代码健壮性与可维护性。

✅ 核心优势

  • 编译时类型检查
  • 更好的 IDE 智能提示
  • 重构更安全
  • 团队协作更清晰

二、TypeScript 基础速览

1. 基本类型

let name: string = "Alice";
let age: number = 25;
let isActive: boolean = true;
let hobbies: string[] = ["coding", "reading"];
let tuple: [string, number] = ["Bob", 30]; // 元组

2. 接口(Interface)——定义对象结构

这是 TS 的核心能力之一。例如,我们定义一个待办事项(Todo)的数据结构:

// types/todo.ts
export interface Todo {
  id: number;
  title: string;
  completed: boolean;
}

🔍 关键点

  • interface 描述对象“长什么样”
  • 所有属性必须符合定义,否则编译报错
  • 支持可选属性(completed?: boolean)、只读(readonly id: number)等

3. 泛型(Generics)——让函数/类更通用

比如你的工具函数 getStorage 和 setStorage

ts

编辑

// utils/storages.ts
export function getStorage<T>(key: string, defaultValue: T): T {
  const item = localStorage.getItem(key);
  return item ? JSON.parse(item) : defaultValue;
}

export function setStorage<T>(key: string, value: T): void {
  localStorage.setItem(key, JSON.stringify(value));
}
  • <T> 表示“任意类型”,调用时自动推断或显式指定
  • getStorage<Todo[]>(STORAGE_KEY, []) → 返回 Todo[] 类型

三、在 React 中使用 TypeScript

React + TS 是现代前端开发的黄金组合。让我们分析你提供的 useTodos 自定义 Hook。

1. 引入类型(注意 import type

import type { Todo } from '../types/todo';
  • 使用 import type 明确表示只导入类型(非运行时值)
  • 构建时会被移除,避免打包冗余代码
  • 这是 TS 3.8+ 的最佳实践

2. useState 添加类型注解

const [todos, setTodos] = useState<Todo[]>(() => getStorage<Todo[]>(STORAGE_KEY, []));
  • useState<Todo[]> 告诉 TS:这个 state 是 Todo 对象数组
  • 初始值通过函数形式传入,避免重复计算
  • getStorage<Todo[]>(...) 确保从 localStorage 读取的数据被当作 Todo[] 处理

💡 如果不加类型,TS 会尝试推断,但初始值为 [] 时可能推断为 never[],导致后续赋值报错!

3. 函数参数与返回值类型

const addTodo = (title: string) => {
  const newTodo: Todo = {
    id: +new Date(),      // number
    title,                // string
    completed: false      // boolean
  };
  setTodos([...todos, newTodo]);
};
  • 参数 title: string 防止传入数字或对象
  • newTodo: Todo 确保对象结构完整(少写字段会报错!)

4. useEffect 与副作用

useEffect(() => {
  setStorage<Todo[]>(STORAGE_KEY, todos);
}, [todos]);
  • 每当 todos 变化,自动同步到 localStorage
  • 类型安全确保存入的数据结构正确

四、项目结构建议(以你的代码为例)

src/
├── types/
│   └── todo.ts          ← 类型定义集中管理
├── utils/
│   └── storages.ts      ← 工具函数 + 泛型
├── hooks/
│   └── useTodos.ts      ← 自定义 Hook + TS
└── components/
    └── TodoList.tsx     ← 组件使用 todos

✅ 最佳实践

  • 类型文件统一放在 types/
  • 工具函数使用泛型提升复用性
  • 自定义 Hook 封装逻辑 + 类型安全

五、常见错误与避坑指南

表格

问题解决方案
“Property 'xxx' does not exist on type 'YYY'”检查接口是否包含该属性,或使用可选链 ?.
数组推断为 never[]显式声明类型:useState<Todo[]>([])
localStorage 数据结构不匹配使用 Zod 或自定义验证函数校验数据
类型太复杂难以维护拆分接口,使用 Pick<T, K>Omit<T, K> 等工具类型

六、进阶:类型守卫与工具类型

当你从 API 获取数据时,不能完全信任后端返回的结构。可使用类型守卫验证:

function isTodoArray(data: any): data is Todo[] {
  return Array.isArray(data) && data.every(item =>
    typeof item.id === 'number' &&
    typeof item.title === 'string' &&
    typeof item.completed === 'boolean'
  );
}

// 使用
const raw = getStorage('todos', []);
if (isTodoArray(raw)) {
  setTodos(raw); // 安全!
}

七、总结

TypeScript 不是增加负担,而是提前发现问题、减少 bug、提升开发体验的利器。从定义一个简单的 interface 开始,到编写类型安全的自定义 Hook(如你的 useTodos),再到构建大型应用,TS 都能为你保驾护航。

🚀 行动建议

  1. 在新项目中直接启用 TS
  2. 旧项目可逐步迁移(.js → .ts
  3. 善用 VS Code 的 TS 提示和快速修复