TypeScript 30分钟上手:零基础也能写出带类型的 React 应用
大家好,我是一名开源项目维护者,也教过不少前端新手。很多人在刚接触 React 时,总被“TypeScript”这个词吓退——觉得它高深、复杂、只适合大厂。其实不然!我当初学的时候,也是从“变量怎么加类型”这种问题开始的。今天这篇教程,就是想告诉你:只要30分钟,你就能用 TypeScript 写出第一个安全、清晰、可维护的 React 小应用。
更重要的是,无论你是想进大厂,还是自己搞个小项目做运营展示页,TypeScript 都能帮你少踩90%的坑。尤其当你和团队协作,或者长期维护一个项目(比如一个用户增长活动页),类型检查就像给代码装上了“导航仪”,再也不怕改一处崩三处。
为什么你需要 TypeScript?
简单说:JavaScript 是自由的,但太自由了容易出错;TypeJS 是 JavaScript 的“超集”,给它加上了类型系统,让代码更安全、更易读、更好维护。
举个例子:
你在写一个用户登录功能,调用 getUserInfo(userId),但不小心传了个字符串 "123" 而不是数字 123。JavaScript 不会报错,但后端可能直接返回 500 错误。而 TypeScript 会在你写代码时就提醒:“喂,这里要数字,你传错了!”
对于运营类页面(比如活动报名、抽奖、问卷收集),逻辑看似简单,但一旦用户量上来,一个小 bug 可能导致数据丢失或用户体验崩坏。用 TypeScript,这些问题在编码阶段就被拦截了。
第一步:搭建开发环境(5分钟搞定)
别担心,现在创建 TypeScript + React 项目比泡面还快!
前提条件
- 安装了 Node.js(建议 v18+)
- 会用终端(命令行)
创建项目
打开终端,运行以下命令:
# 使用 Vite 创建项目(超快!)
npm create vite@latest my-ts-app -- --template react-ts
# 进入项目目录
cd my-ts-app
# 安装依赖
npm install
# 启动开发服务器
npm run dev
💡 小贴士:Vite 是新一代构建工具,启动速度秒杀 Webpack。
react-ts模板已经预配置好 TypeScript 和 React,开箱即用。
启动成功后,你会看到类似这样的提示:
Local: http://localhost:5173/
打开浏览器访问这个地址,就能看到欢迎页面了!
目录结构简览
my-ts-app/
├── src/
│ ├── App.tsx ← 主组件(TypeScript + JSX)
│ ├── main.tsx ← 入口文件
│ └── vite-env.d.ts ← 类型声明文件
├── tsconfig.json ← TypeScript 配置文件
└── package.json
重点看 tsconfig.json —— 这是 TypeScript 的“规则手册”。默认配置对新手非常友好,无需修改。
第二步:TypeScript 核心概念(用最简单的话讲清楚)
1. 类型注解:给变量“贴标签”
在 JavaScript 中,你可以这样写:
let name = "张三";
let age = 25;
但在 TypeScript 中,你可以明确告诉编译器:“这个变量只能是字符串”:
let name: string = "张三";
let age: number = 25;
不过,TypeScript 很聪明!如果你初始化时赋了值,它会自动推断类型,所以通常可以省略:
let name = "张三"; // 自动推断为 string
let age = 25; // 自动推断为 number
但如果后续你想把 age 改成字符串,就会报错:
age = "二十五"; // ❌ Error! Type 'string' is not assignable to type 'number'.
2. 常见基础类型
| 类型 | 说明 | 示例 |
|---|---|---|
string |
字符串 | "hello", 'world' |
number |
数字 | 42, 3.14 |
boolean |
布尔值 | true, false |
null / undefined |
空值 | null, undefined |
any |
任意类型(慎用!) | let x: any = "随便" |
⚠️ 避坑指南:尽量不要用
any!它等于关掉了 TypeScript 的保护,失去了使用 TS 的意义。
3. 接口(Interface):定义对象的“形状”
假设你要表示一个“运营活动”的数据结构:
interface Campaign {
id: number;
title: string;
isActive: boolean;
endDate: string; // ISO 日期格式,如 "2024-12-31"
}
现在,任何符合这个“形状”的对象都可以被接受:
const summerSale: Campaign = {
id: 1,
title: "夏日大促",
isActive: true,
endDate: "2024-08-31"
};
如果少写一个字段,或者类型不对,TS 会立刻报错。
4. 函数类型:参数和返回值也要有类型
// 定义一个函数,接收 Campaign,返回是否还在进行中
function isCampaignActive(campaign: Campaign): boolean {
const today = new Date().toISOString().split('T')[0];
return campaign.isActive && campaign.endDate >= today;
}
调用时,如果传错参数,马上报错:
isCampaignActive({ id: 1, title: "测试" }); // ❌ 缺少 isActive 和 endDate!
第三步:实战!用 TypeScript + React 写一个“运营活动列表”
现在,我们来做一个真实场景的小应用:展示多个运营活动,并标记哪些还在进行中。
步骤 1:定义数据模型
在 src/ 目录下新建 types.ts:
// types.ts
export interface Campaign {
id: number;
title: string;
description: string;
isActive: boolean;
endDate: string; // YYYY-MM-DD
}
步骤 2:准备模拟数据
在 src/data.ts 中:
// data.ts
import { Campaign } from './types';
export const campaigns: Campaign[] = [
{
id: 1,
title: "618年中大促",
description: "全场5折起,限时三天!",
isActive: true,
endDate: "2024-06-18"
},
{
id: 2,
title: "开学季礼包",
description: "新用户注册即送100元券",
isActive: false,
endDate: "2024-09-10"
}
];
注意:campaigns 被显式标注为 Campaign[](Campaign 数组),确保数组里每个元素都符合接口。
步骤 3:编写工具函数
在 src/utils.ts 中:
// utils.ts
import { Campaign } from './types';
export function isCampaignActive(campaign: Campaign): boolean {
const today = new Date().toISOString().split('T')[0];
return campaign.isActive && campaign.endDate >= today;
}
步骤 4:改造 App 组件
打开 src/App.tsx,替换为以下内容:
// App.tsx
import { campaigns } from './data';
import { isCampaignActive } from './utils';
import './App.css';
function App() {
return (
<div className="app">
<h1>运营活动中心</h1>
<div className="campaign-list">
{campaigns.map(campaign => (
<div
key={campaign.id}
className={`campaign-card ${isCampaignActive(campaign) ? 'active' : 'inactive'}`}
>
<h2>{campaign.title}</h2>
<p>{campaign.description}</p>
<span className="status">
{isCampaignActive(campaign) ? '进行中' : '已结束'}
</span>
</div>
))}
</div>
</div>
);
}
export default App;
步骤 5:加点样式(可选)
在 src/App.css 末尾添加:
.campaign-card {
border: 1px solid #ddd;
padding: 16px;
margin: 16px 0;
border-radius: 8px;
}
.campaign-card.active {
border-color: #4caf50;
background-color: #f9f9f9;
}
.campaign-card.inactive {
opacity: 0.6;
}
.status {
display: inline-block;
margin-top: 8px;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
}
.campaign-card.active .status {
background-color: #e8f5e9;
color: #2e7d32;
}
.campaign-card.inactive .status {
background-color: #fce4ec;
color: #c62828;
}
运行效果
保存后,浏览器会自动刷新。你会看到两个活动卡片,一个绿色“进行中”,一个灰色“已结束”。
✅ 恭喜!你已经用 TypeScript 写出了一个类型安全的 React 应用!
新手常见问题 & 解决方案
Q1:.ts 和 .tsx 有什么区别?
.ts:纯 TypeScript 文件(不能写 JSX).tsx:支持 JSX 语法的 TypeScript 文件(用于 React 组件)
✅ 规则:只要文件里用了 <div> 这种 JSX 语法,就必须用 .tsx 后缀。
Q2:为什么我的组件 props 报错?
常见错误:
function CampaignCard(title) { // ❌ 没有类型
return <h2>{title}</h2>;
}
正确做法:
interface Props {
title: string;
isActive: boolean;
}
function CampaignCard({ title, isActive }: Props) {
return <h2>{title}</h2>;
}
💡 技巧:VS Code 安装 “ESLint” 和 “Prettier” 插件,能实时提示类型错误。
Q3:如何处理 API 返回的数据?
实际项目中,数据来自后端。你可以这样写:
// 假设 API 返回 { data: Campaign[] }
async function fetchCampaigns(): Promise<Campaign[]> {
const res = await fetch('/api/campaigns');
const json = await res.json();
return json.data; // TypeScript 会检查 json.data 是否符合 Campaign[]
}
🔒 安全提示:生产环境中,建议用
zod或io-ts做运行时验证,因为网络数据不可信。
Q4:TypeScript 让我写更多代码,值得吗?
短期看,确实多写了几行类型定义。但长期看:
| 场景 | JavaScript | TypeScript |
|---|---|---|
| 改一个字段名 | 可能漏改,运行时报错 | 编译时报错,一键修复 |
| 团队协作 | 靠文档或口头沟通 | 类型即文档,IDE 自动提示 |
| 重构代码 | 心惊胆战 | 安全无忧 |
尤其是运营类项目,经常要快速迭代、多人协作,TypeScript 的价值会指数级放大。
下一步学习建议
你已经跨过了最难的第一步!接下来,我建议:
- 深入学习泛型:比如
useState<Campaign[]>([]),让 Hook 也带类型 - 尝试联合类型和字面量类型:
type CampaignStatus = 'active' | 'ended' | 'draft'; - 集成到真实项目:把你现有的 React 项目逐步迁移到 TS(可以先改
.js为.ts,再慢慢加类型) - 阅读官方文档:TypeScript Handbook(中文版很完善)
最后的话
我写这篇教程,是因为见过太多新手被 TypeScript 吓退,结果在项目后期吃尽苦头。其实,TypeScript 不是门槛,而是拐杖——它扶着你写出更健壮的代码,尤其是在做运营、活动、表单这类“看似简单实则易错”的场景。
记住:30分钟上手,不代表30分钟精通。但只要你迈出第一步,后面的路会越走越稳。
现在,打开你的编辑器,创建那个 my-ts-app 吧!遇到问题,欢迎在评论区留言——作为一个开源维护者,我很乐意帮你解答。
Happy coding! 🚀

评论 0