React 的 TypeScript 知识
编者注: ==TypeScript 是 React 开发者的必备技能==,它能帮你 1) 在代码运行前捕获错误,2) 获得更好的编辑器智能提示,3) 使代码更易于维护。本文介绍了 TypeScript 的核心概念和 React 中的最佳实践,助你快速上手。
我经常看到这样的帖子——说实话,这让我很心痛 🥲
如果你还在用 JavaScript 开发 React 应用,那你是在给自己增加难度。
学习 TypeScript 所花的时间,100% 会从你节省的调试时间中赚回来。
所以这里有一份指南帮你完成转换。
只讲精华。不废话 😉。
TypeScript 工作原理
TypeScript 在 JavaScript 基础上添加了额外语法,帮助你在应用崩溃前捕获错误。
最终它会被编译成 JavaScript。
最棒的是?==你可以逐步迁移到 TypeScript==。
更多细节请参考这个指南。
为什么你应该尽快切换到 TypeScript
优势 #1: 在生产环境前捕获错误
这段 JavaScript 代码看起来没问题...直到它崩溃:
function printFirstChar(users, userIndex) {
const name = users[userIndex];
console.log(name[0]);
}
const users = ["Fatou", "Bob"];
printFirstChar(users, 5); // ❌ 运行时错误: 无法读取 undefined 的属性
TypeScript 版本:
function printFirstChar(users: string[], userIndex: number) {
const name = users[userIndex];
if (name != null) {
console.log(name[0]);
}
}
TypeScript 不会修复你的逻辑,但会给你==早期警告==比如"嘿,这里可能是 undefined 👀"
优势 #2: 获得库的自动补全
特别有用当你探索新库时。
如果那个库有 TypeScript 类型定义,你就能在大多数编辑器中获得自动补全。
优势 #3: 让你的代码更能适应变化
当你正确添加类型后,TypeScript 就像安全网。
例如:
没有 TypeScript,这段代码可能悄无声息地出错
function getLabel(status) {
switch (status) {
case "idle":
return "Idle";
case "loading":
return "Loading";
case "success":
return "Success";
}
}
getLabel("loading"); // ✅ 正常工作
// 几个月后...
getLabel("error"); // ❌ 返回 undefined,但没有 JS 错误
使用 TypeScript,你需要处理变化
type Status = "idle" | "loading" | "success";
function getLabel(status: Status): string {
switch (status) {
case "idle":
return "Idle";
case "loading":
return "Loading";
case "success":
return "Success";
case "error":
return "Error";
}
}
// 几个月后...
type Status = "idle" | "loading" | "success" | "error"; // 添加了 'error'
// ✅ TypeScript 会显示错误: "并非所有代码路径都返回值"
如果你忘记处理某个情况,TypeScript 会提醒你。这正是你想要的 😄。
当我们忘记处理某个情况时的 TypeScript 错误
React 开发需要掌握的 TypeScript 概念
让我们覆盖==最基础==的入门知识。
✅ 类型
类型让你能描述数据的形状并强制执行。
TypeScript 中有原生类型和用户定义类型。
原生类型
这些类型是默认提供的。
const name = "Ada"; // string
const age: number = 31; // number
const isActive = true; // boolean
const scores: number[] = [90, 85, 100]; // number[]
💡 在上面的一些例子中,我显式地标注了类型(如 const age: number = 31)。
在实际代码中你不需要这样做——TypeScript 可以通过值推断出类型。
自定义类型
你可以用原生类型创建新类型。
type User = {
id: number;
name: string;
};
const user: User = {
id: 1,
name: "Fatou",
};
❌ 避免使用 any
除非你正在迁移过程中,否则不要使用 any。
它只是伪装成 JavaScript。
改用 unknown ——它会强制你在使用前检查类型。
// ❌ 不好: 没有警告,运行时崩溃
function logLength(value: any) {
console.log(value.length);
}
logLength(123);
// ✅ 更安全
function logLength(value: unknown) {
if (typeof value === 'string' || Array.isArray(value)) {
console.log(value.length);
}
}
// ✅✅ 最好: 使用显式类型
function logLength(value: string | ArrayLike<unknown>){
console.log(value.length);
}
🧩 联合与交叉类型
联合类型让一个值可以是多种类型之一。
type Status = 'idle' | 'loading' | 'error';
let currentStatus: Status = 'idle';
交叉类型将多个类型合并为一个。
type Name = { name: string };
type Age = { age: number };
type Person = Name & Age;
const person: Person = { name: 'Ada', age: 30 };
🧱 接口
接口定义对象的结构。
interface User {
name: string;
age: number;
}
你可以扩展接口:
interface Admin extends User {
role: string;
}
什么时候用 type 还是 interface?区别不大。
🔁 泛型
泛型让你能编写可复用的灵活类型。
假设你要为 API 响应添加类型。
没有泛型:
type UserResponse = {
status: number;
data: User;
error?: string;
};
type ProductResponse = {
status: number;
data: Product;
error?: string;
};
太多重复代码了。
使用泛型:
type ApiResponse<T> = {
status: number;
data: T;
error?: string;
};
type UserResponse = ApiResponse<User>;
type ProductResponse = ApiResponse<Product>;
你也可以在函数中使用泛型:
function wrap<T>(value: T): T[] {
return [value];
}
wrap(42); // 类型: number[]
wrap('hi'); // 类型: string[]
我喜欢把泛型想象成==函数的参数==——不过是针对类型的 😉。
🔧 函数类型
你可以为函数参数和返回值添加类型。
你总是需要为参数添加类型,但不必总是为返回值添加类型。
// 这样可行
function add(a: number, b: number): number {
return a + b;
}
// 这样也可行: 你不需要为返回值添加类型
function add(a: number, b: number) {
return a + b; // 推断为 number
}
你也可以为函数变量添加类型:
const add: (a: number, b: number) => number = (a, b) => a + b;
根据上下文,你可能想为返回值添加类型。==不确定时,就加上返回类型==。
如何在 React 中使用 TypeScript
Props
Props 就是对象。像为其他对象一样添加类型。
使用 type:
type GreetingProps = {
name: string;
};
function Greeting({ name }: GreetingProps) {
return <p>你好 {name}</p>;
}
使用 interface:
interface GreetingProps {
name: string;
}
function Greeting({ name }: GreetingProps) {
return <p>你好 {name}</p>;
}
State
useState 接受泛型类型,但 TypeScript 也能推断它。
const [count, setCount] = useState(0); // 推断为 number
但如果不能推断,就显式声明:
const [count, setCount] = useState<number>(0);
组件变量和函数
就像普通 TypeScript 一样:
const users: User[] = [{name: "Fatou", age: 31}, {name: "Bob", age: 25} ];
const age = 30; // 推断为 number
当你有一些像事件处理函数时,可以悬停在 DOM 元素上看要使用的类型。
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
console.log('点击了!', event);
};
// 或者:
const handleClick: React.MouseEventHandler<HTMLButtonElement> = (event) => {
console.log('点击了!', event);
};
跨组件复用类型
你可以扩展或组合类型/接口来避免重复。
使用接口:
interface ButtonProps {
label: string;
}
interface IconButtonProps extends ButtonProps {
icon: React.ReactNode;
}
Hooks
Hooks 就是函数,所以你可以使用适用于函数的相同 TypeScript 工具。
原生 hooks
几乎所有原生 hooks 都有泛型参数。
const [count, setCount] = useState<number>(0);
// 或者让 TS 推断:
const [count, setCount] = useState(0);
const inputRef = useRef<HTMLInputElement>(null);
const [state, dispatch] = useReducer(
(state: number, action: 'inc' | 'dec') => {
switch (action) {
case 'inc':
return state + 1;
case 'dec':
return state - 1;
}
},
0
);
自定义 hooks
function useToggle(initial: boolean): [boolean, () => void] {
const [state, setState] = useState(initial);
const toggle = () => setState((prev) => !prev);
return [state, toggle];
}
const [isOpen, toggleOpen] = useToggle(false);
延伸学习资源
如果你想深入学习 TypeScript,这里有一些优质资源:
- 🧠 TypeScript 官方文档: 最佳起点。清晰、结构良好且包含大量示例。还包括一个演练场供你测试。
- 🎯 Total TypeScript: Matt Pocock 创建的优秀网站——特别适合 React 开发者。有免费初学者课程、高级模式和许多真实案例。
- ✨ TypeScript 风格指南: 关于如何编写干净一致的 TypeScript 代码的简短实用建议。当你开始构建真实应用时强烈推荐。
- 🧩 Type Challenges: 从入门到疯狂的 TypeScript 谜题集合。如果你喜欢通过解决问题来学习并想锻炼类型系统技能,这很棒。
- 📘 Effective TypeScript: Dan Vanderkam 的书+博客。更高级,但教你如何编写更好的 TypeScript。掌握基础后值得一看。
- ⚛️ React TypeScript 备忘单: 专为 React 开发者定制。涵盖从组件类型到上下文和 hooks 的所有内容。非常新手友好,作为快速参考超级有用。
💡 确保也查看我的101 React 技巧与窍门中的 TypeScript 部分 😉。
总结
在 React 中使用 TypeScript 是你能做的最佳升级之一。
它帮你==提前捕获错误==,提供==更好的工具支持==,让你的代码==更易于维护==。
最棒的是?==你不需要一开始就学习所有内容==。从基础开始——类型、接口、泛型——然后逐步深入。
网上有很多例子能帮你解决问题,AI 工具也能快速帮你突破障碍。
如有任何问题欢迎交流 🙂。