为什么选择 React + TypeScript 🤔
想象一下这个场景:你辛辛苦苦写了一个登录功能,测试的时候一切正常,上线后突然收到用户反馈 ——"点登录没反应啊!" 排查了半天发现,后端返回的用户信息里,isLogin字段有时候是布尔值true,有时候是字符串"true",而你写的判断逻辑是if (user.isLogin)... 这种因为类型混乱导致的 Bug,在 JavaScript 开发中简直像家常便饭 🍚
JavaScript 作为一门弱类型语言,就像个调皮的孩子 —— 你让它拿个苹果,它可能给你带个橙子回来,还理直气壮地说 "反正都是水果嘛~" 而当项目规模越来越大,这种 "不拘小节" 就会变成灾难:函数参数传错类型、对象属性拼写错误、数组里混进奇怪的值... 调试这些问题的时间,够你喝十杯奶茶了 🥤
这时候,React + TypeScript 的组合就像给代码装上了 "安全气囊" 和 "导航系统":
-
React 帮你把 UI 拆成一个个可复用的组件,让界面开发像搭积木一样简单 🧱
-
TypeScript 则给 JavaScript 加上类型约束,在你写代码的时候就提前报错,把 Bug 扼杀在摇篮里
对于大型项目来说,这对组合堪称 "黄金搭档"—— 微软爸爸开发的 TypeScript,配上 Facebook 打造的 React,就像肯德基的炸鸡配可乐,绝配!
React 与 TypeScript 初相识 👋
React:构建用户界面的神器 ✨
React 是个啥?简单说就是个用来写网页界面的 JavaScript 库。它最核心的概念是 "组件"—— 你可以把组件理解成一个函数,接收一些参数(props),然后返回一段描述界面的代码(JSX)。
比如一个最简单的组件长这样:
// 这就是一个React组件
function Hello() {
return <h1>Hello React! 👋</h1>;
}
就像乐高积木一样,你可以把这些小组件组合起来,搭建出复杂的页面。而且 React 采用 "单向数据流",数据变化时界面会自动更新,再也不用手动操作 DOM 了,简直是前端开发者的福音 🙏
TypeScript:JavaScript 的超强升级版 🦸♂️
TypeScript(简称 TS)是微软开发的 JavaScript 超集 —— 啥意思?就是 TS 完全兼容 JS 的语法,你可以把 JS 代码直接改成 TS 代码,它照样能跑。但 TS 多了个大杀器:类型约束。
比如在 JS 里,你可以这么写:
let age = 18;
age = "十八"; // 没问题,JS默默接受了
但在 TS 里,这就会直接报错:
let age: number = 18;
age = "十八"; // 红波浪线警告!Type 'string' is not assignable to type 'number'
就像给变量贴上了标签,"这个只能装数字,那个只能装字符串",从此代码变得规矩起来。而且 TS 的类型检查是在编译阶段进行的,最终会转成纯 JS 运行,不影响浏览器兼容性,是不是很贴心?
React + TypeScript 开发环境搭建 🛠️
准备工作 📋
首先得确保你电脑上装了 Node.js 和 npm(一般装 Node.js 会自带 npm)。检查一下:
node -v # 输出版本号就说明装好了,比如 v18.16.0
npm -v # 比如 9.5.1
如果没装,去Node.js 官网下载安装,一路下一步就行,跟装 QQ 似的简单。
创建 React + TS 项目 🚀
最方便的方式是用官方脚手架 Create React App,一行命令搞定:
npx create-react-app my-ts-app --template typescript
这里的--template typescript就是告诉脚手架:"给我来个 TS 版本的!"
创建完成后,进入项目目录并启动:
cd my-ts-app
npm start
稍等片刻,浏览器会自动打开http://localhost:3000,你会看到一个带 TS 标志的 React 欢迎页面,说明环境搭建成功啦!🎉
项目目录小科普 📂
生成的项目里,有几个文件要特别注意:
.tsx后缀的文件:这是 TS 版的 JSX 文件,React 组件就写在这里tsconfig.json:TS 的配置文件,里面可以设置类型检查的严格程度等react-app-env.d.ts:声明文件,让 TS 认识 React 相关的类型
React + TypeScript 实战开发 👨💻
基础语法大揭秘 🔍
先从 TS 的基础类型学起,这些在 React 组件里都会用到:
// 基本类型
let count: number = 10; // 数字
const title: string = "Hello ts"; // 字符串
const isDone: boolean = true; // 布尔值
// 数组
const list: number[] = [1, 2, 3]; // 全是数字的数组
const strList: Array<string> = ["a", "b", "c"]; // 另一种写法
// 元组(固定长度和类型的数组)
const tuple: [number, string] = [1, "乡乡"]; // 第一个必须是数字,第二个必须是字符串
// 对象类型(用interface约束)
interface User {
name: string;
age: number;
isSingle?: boolean; // 问号表示可选属性
}
const user: User = {
name: "乡乡",
age: 18,
// isSingle可以不写,因为是可选的
};
是不是很简单?就像给变量加了个 "身份证",规定了它的类型。
React 组件开发 🏗️
函数组件与 Props 类型约束 📦
在 React 中,组件接收的参数叫 props,用 TS 约束 props 类型是最常见的操作。我们通常用interface来定义 props 的结构:
// NameEditComponent.tsx
import React from "react";
// 定义props的类型
interface Props {
userName: string; // 必须传一个字符串类型的userName
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void; // 一个函数,接收输入事件
}
// 用React.FC<Props>来指定这是一个React组件,并且props要符合Props类型
const NameEditComponent: React.FC<Props> = ({ userName, onChange }) => {
return (
<>
<label>Update name:</label>
<input
type="text"
value={userName}
onChange={onChange}
placeholder="请输入姓名"
/>
</>
);
};
export default NameEditComponent;
这里的React.ChangeEvent<HTMLInputElement>是 React 内置的类型,表示输入框的 change 事件,里面包含了输入框的值等信息。有了这个约束,当你在组件里用e.target.value时,TS 会知道这是字符串类型,还会给你自动补全提示,简直不要太爽!
父组件如何传值 👨👦
上面定义的NameEditComponent需要父组件传userName和onChange,父组件可以这么写:
// App.tsx
import { useState } from "react";
import NameEditComponent from "./components/NameEditComponent";
function App() {
// 用useState定义状态,指定类型为string
const [name, setName] = useState<string>("initialName");
// 定义事件处理函数,指定参数类型为React.ChangeEvent<HTMLInputElement>
const setUsernameState = (event: React.ChangeEvent<HTMLInputElement>) => {
setName(event.target.value); // 这里event.target.value会被TS识别为string
};
return (
<>
{/* 传值给子组件,TS会检查类型是否匹配 */}
<NameEditComponent userName={name} onChange={setUsernameState} />
</>
);
}
export default App;
注意useState<string>("initialName")这里,我们指定了状态的类型是 string。其实 TS 很聪明,如果你不传类型,它会根据初始值自动推断,但显式指定类型能让代码更清晰,尤其是初始值为null或undefined的时候。
枚举类型小技巧 🎭
有时候我们需要一些固定的状态值,比如 "待处理"、"已完成"、"已拒绝",这时候可以用 TS 的枚举(但更推荐用as const的对象,性能更好):
// 推荐写法:用as const让对象的值变成只读的字面量类型
const Status = {
Pending: 0,
Fulfilled: 1,
Rejected: 2
} as const;
// 这样pStatus的类型就是0,而不是number
const pStatus = Status.Pending;
这样当你写if (pStatus === Status.Pending)时,TS 会帮你检查是否拼写错误,比纯 JS 安全多了。
常见问题与解决方案 🧐
- "JSX 元素类型没有任何构造签名" 报错
通常是因为忘了导入 React,或者组件类型定义错了。解决:确保组件用React.FC定义,或者在文件顶部加import React from 'react'。 - 事件处理函数里拿不到
e.target.value
因为没指定事件类型。解决:给参数加上e: React.ChangeEvent<HTMLInputElement>之类的类型。 - 可选属性报错 "属性不存在"
当你访问user.isSingle时,因为它是可选属性,TS 会提示可能不存在。解决:用user.isSingle?.toString()(可选链操作符),或者先判断if (user.isSingle)。 - 组件传参时类型不匹配
比如子组件要number,你传了string。解决:检查传参的类型,或者用Number()、String()等方法转换类型。
总结与展望 🏁
恭喜你!看到这里,你已经掌握了 React + TypeScript 的入门知识 🎉 总结一下:
-
TypeScript 是 JS 的超集,核心是类型约束,能在编译时发现错误
-
React 组件用 TS 约束后,props 和 state 的类型更清晰,减少 Bug
-
interface用来定义对象类型,React.FC<Props>用来定义组件 -
事件类型如
React.ChangeEvent能让事件处理更安全
接下来你可以尝试:
-
用 TS 写一个 TodoList(经典练手项目)
-
学习 React Router 的 TS 用法
-
探索状态管理库(如 Redux Toolkit)与 TS 的结合
记住,TypeScript 虽然一开始会让你多写一些代码,但从长远来看,它能帮你节省大量调试时间,尤其在团队协作和大型项目中,优势简直不要太明显。
最后送大家一句名言: "早用 TS 早轻松,晚用 TS 哭着冲" 😂 祝大家在 React + TypeScript 的世界里玩得开心,写出更健壮的代码!