React搭配TypeScript使用教程及實(shí)戰(zhàn)案例
一、React與TypeScript搭配核心優(yōu)勢(shì)
TypeScript(簡(jiǎn)稱TS)是JavaScript的超集,核心優(yōu)勢(shì)是靜態(tài)類型檢查,能在開發(fā)階段發(fā)現(xiàn)類型錯(cuò)誤,避免運(yùn)行時(shí)bug;同時(shí)提供更清晰的代碼提示、更好的代碼可維護(hù)性和可擴(kuò)展性,尤其適合中大型React項(xiàng)目。
React與TS搭配的核心價(jià)值:
- 組件Props類型約束:明確組件接收的參數(shù)類型、必填項(xiàng),減少傳參錯(cuò)誤;
- 狀態(tài)(State)類型定義:規(guī)范狀態(tài)的數(shù)據(jù)結(jié)構(gòu),避免狀態(tài)賦值錯(cuò)誤;
- 減少類型相關(guān)注釋:類型定義即文檔,提升團(tuán)隊(duì)協(xié)作效率;
- IDE友好提示:自動(dòng)補(bǔ)全組件屬性、方法,降低開發(fā)成本。
二、環(huán)境搭建(React + TS)
2.1 快速創(chuàng)建React+TS項(xiàng)目
使用create-react-app快速初始化,自帶TS配置,無(wú)需手動(dòng)配置webpack、tsconfig.json:
# 方式1:npx(推薦,無(wú)需全局安裝) npx create-react-app react-ts-demo --template typescript # 方式2:yarn yarn create react-app react-ts-demo --template typescript
項(xiàng)目創(chuàng)建完成后,核心文件說(shuō)明:
- .tsx:React組件文件后綴(包含JSX語(yǔ)法,必須用.tsx);
- .ts:非組件的TS文件(如工具函數(shù)、類型定義);
- tsconfig.json:TS的核心配置文件(指定編譯選項(xiàng)、類型檢查規(guī)則);
- react-app-env.d.ts:React與TS的類型聲明文件(自動(dòng)生成,無(wú)需修改)。
2.2 核心配置(tsconfig.json關(guān)鍵項(xiàng))
無(wú)需手動(dòng)修改默認(rèn)配置,重點(diǎn)了解以下關(guān)鍵項(xiàng),便于后續(xù)自定義:
{
"compilerOptions": {
"target": "ESNext", // 目標(biāo)JS版本
"module": "ESNext", // 模塊規(guī)范
"jsx": "react-jsx", // 支持JSX語(yǔ)法(React 17+ 推薦)
"strict": true, // 開啟嚴(yán)格模式(推薦,強(qiáng)制類型檢查)
"esModuleInterop": true, // 兼容CommonJS模塊
"skipLibCheck": true, // 跳過(guò)第三方庫(kù)類型檢查
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true, // 支持導(dǎo)入JSON文件
"isolatedModules": true,
"noEmit": true // 不生成編譯后的JS文件(由create-react-app處理)
},
"include": ["src"] // 需要編譯的文件目錄
}
三、React+TS基礎(chǔ)使用(核心語(yǔ)法)
3.1 組件Props類型定義(最常用)
通過(guò)interface(接口)定義Props類型,明確組件接收的參數(shù),支持必填/可選、默認(rèn)值、聯(lián)合類型等。
import React from 'react';
// 1. 定義Props接口(首字母大寫,約定俗成)
interface UserCardProps {
// 必填項(xiàng)(無(wú)?)
name: string;
age: number;
// 可選項(xiàng)(加?)
gender?: 'male' | 'female' | 'other'; // 聯(lián)合類型,限制可選值
// 函數(shù)類型(定義回調(diào)函數(shù))
onBtnClick: (id: number) => void;
}
// 2. 組件接收Props,指定類型為UserCardProps
const UserCard: React.FC<UserCardProps> = (props) => {
// 解構(gòu)Props(更簡(jiǎn)潔)
const { name, age, gender = 'other', onBtnClick } = props;
return (
<div className="user-card">
<h3>{name}</h3>
<p>年齡:{age}</p>
<p>性別:{gender}</p>
<button onClick={() => onBtnClick(123)}>點(diǎn)擊</button>
</div>
);
};
export default UserCard;
說(shuō)明:React.FC 是React函數(shù)組件的類型,泛型參數(shù)即為Props的類型;可選參數(shù)通過(guò)?標(biāo)記,可設(shè)置默認(rèn)值避免undefined。
3.2 組件State類型定義
使用useState時(shí),TS會(huì)自動(dòng)推斷狀態(tài)類型(類型推導(dǎo)),復(fù)雜狀態(tài)(如對(duì)象、數(shù)組)需手動(dòng)指定類型。
import React, { useState } from 'react';
// 1. 簡(jiǎn)單狀態(tài)(自動(dòng)推斷類型)
const SimpleState = () => {
// TS自動(dòng)推斷count為number類型,setCount只能接收number
const [count, setCount] = useState(0);
// 錯(cuò)誤示例:setCount('123') → 類型不匹配,開發(fā)階段報(bào)錯(cuò)
return <button onClick={() => setCount(count + 1)}>計(jì)數(shù):{count}</button>;
};
// 2. 復(fù)雜狀態(tài)(對(duì)象類型,手動(dòng)指定)
interface UserState {
name: string;
age: number;
isLogin: boolean;
}
const ComplexState = () => {
// 手動(dòng)指定狀態(tài)類型為UserState,初始值需符合該類型
const [user, setUser] = useState<UserState>({
name: '張三',
age: 20,
isLogin: false,
});
// 修改狀態(tài)(必須符合UserState類型)
const login = () => {
setUser({ ...user, isLogin: true });
};
return (
<div>
<p>{user.name}({user.age}歲)</p>
<button onClick={login}>{user.isLogin ? '已登錄' : '登錄'}</button>
</div>
);
};
export default ComplexState;
3.3 事件處理類型定義
React事件有固定的TS類型(如點(diǎn)擊事件React.MouseEvent、輸入事件React.ChangeEvent),需指定事件類型和目標(biāo)元素類型。
import React, { useState } from 'react';
const EventDemo = () => {
const [inputValue, setInputValue] = useState('');
// 1. 點(diǎn)擊事件(React.MouseEvent,可指定目標(biāo)元素類型)
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log('按鈕點(diǎn)擊', e.target.innerText);
};
// 2. 輸入框變化事件(React.ChangeEvent,目標(biāo)為輸入框)
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// e.target.value 自動(dòng)推斷為string類型
setInputValue(e.target.value);
};
return (
<div>
<input
type="text"
value={inputValue}
onChange={handleInputChange}
placeholder="請(qǐng)輸入內(nèi)容"
/>
<button onClick={handleClick}>提交</button>
</div>
);
};
export default EventDemo;
3.4 自定義Hook類型定義
自定義Hook返回值為多個(gè)數(shù)據(jù)時(shí),需指定返回值類型(可通過(guò)元組、對(duì)象),確保使用時(shí)類型正確。
import React, { useState, useEffect } from 'react';
// 自定義Hook:獲取窗口寬度
// 定義返回值類型(元組類型,固定順序)
type UseWindowWidthReturn = [number, () => void];
const useWindowWidth = (): UseWindowWidthReturn => {
const [width, setWidth] = useState(window.innerWidth);
const updateWidth = () => {
setWidth(window.innerWidth);
};
useEffect(() => {
window.addEventListener('resize', updateWidth);
return () => window.removeEventListener('resize', updateWidth);
}, []);
// 返回值必須符合UseWindowWidthReturn類型
return [width, updateWidth];
};
// 使用自定義Hook
const WindowWidthDemo = () => {
// 自動(dòng)推斷width為number,updateWidth為() => void
const [width, updateWidth] = useWindowWidth();
return <p>當(dāng)前窗口寬度:{width}px</p>;
};
export default WindowWidthDemo;
四、React+TS實(shí)戰(zhàn)案例(2個(gè)核心場(chǎng)景)
案例1:TodoList(基礎(chǔ)綜合案例)
涵蓋Props、State、事件處理、數(shù)組類型,適合新手入門,完整實(shí)現(xiàn)“添加、刪除、切換完成狀態(tài)”功能。
import React, { useState } from 'react';
// 1. 定義Todo類型(單個(gè)任務(wù))
interface Todo {
id: number;
text: string;
done: boolean;
}
// 2. 定義TodoItem組件Props
interface TodoItemProps {
todo: Todo;
onToggle: (id: number) => void;
onDelete: (id: number) => void;
}
// 3. 子組件:TodoItem
const TodoItem: React.FC<TodoItemProps> = ({ todo, onToggle, onDelete }) => {
return (
<li style={{ textDecoration: todo.done ? 'line-through' : 'none' }}>
<input
type="checkbox"
checked={todo.done}
onChange={() => onToggle(todo.id)}
/>
<span>{todo.text}</span>
<button onClick={() => onDelete(todo.id)} style={{ marginLeft: 10 }}>
刪除
</button>
</li>
);
};
// 4. 父組件:TodoList
const TodoList: React.FC = () => {
// 狀態(tài):todo列表(數(shù)組類型,元素為Todo)
const [todos, setTodos] = useState<Todo[]>([
{ id: 1, text: '學(xué)習(xí)React+TS', done: false },
{ id: 2, text: '完成實(shí)戰(zhàn)案例', done: true },
]);
// 狀態(tài):輸入框內(nèi)容
const [inputText, setInputText] = useState('');
// 添加Todo
const addTodo = (e: React.FormEvent) => {
e.preventDefault(); // 阻止表單默認(rèn)提交
if (!inputText.trim()) return;
const newTodo: Todo = {
id: Date.now(), // 用時(shí)間戳作為唯一id
text: inputText,
done: false,
};
setTodos([...todos, newTodo]);
setInputText(''); // 清空輸入框
};
// 切換Todo完成狀態(tài)
const toggleTodo = (id: number) => {
setTodos(todos.map((todo) => (todo.id === id ? { ...todo, done: !todo.done } : todo)));
};
// 刪除Todo
const deleteTodo = (id: number) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
return (
<div style={{ maxWidth: 500, margin: '0 auto', padding: 20 }}>
<h2>TodoList(React+TS)</h2>
<form onSubmit={addTodo} style={{ marginBottom: 20 }}>
<input
type="text"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
placeholder="請(qǐng)輸入任務(wù)"
style={{ padding: 8, width: 300 }}
/>
<button type="submit" style={{ padding: 8, marginLeft: 10 }}>
添加
</button>
</form>
<ul>
{todos.map((todo) => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={toggleTodo}
onDelete={deleteTodo}
/>
))}
</ul>
</div>
);
};
export default TodoList;
案例2:用戶列表(接口請(qǐng)求+復(fù)雜類型)
涵蓋接口請(qǐng)求(fetch/axios)、加載狀態(tài)、錯(cuò)誤處理、復(fù)雜對(duì)象類型,貼近真實(shí)項(xiàng)目場(chǎng)景,使用axios請(qǐng)求接口(需先安裝:npm install axios @types/axios)。
import React, { useState, useEffect } from 'react';
import axios from 'axios';
// 1. 定義用戶類型(接口返回?cái)?shù)據(jù)結(jié)構(gòu))
interface User {
id: number;
name: string;
username: string;
email: string;
phone: string;
website: string;
}
// 2. 定義接口返回類型(假設(shè)接口返回{ data: User[] })
interface UserResponse {
data: User[];
}
const UserList: React.FC = () => {
// 狀態(tài):用戶列表
const [users, setUsers] = useState<User[]>([]);
// 狀態(tài):加載狀態(tài)
const [loading, setLoading] = useState<boolean>(true);
// 狀態(tài):錯(cuò)誤信息
const [error, setError] = useState<string | null>(null);
// 接口請(qǐng)求( useEffect 模擬組件掛載時(shí)請(qǐng)求)
useEffect(() => {
const fetchUsers = async () => {
try {
setLoading(true);
// 調(diào)用接口(示例接口:JSONPlaceholder)
const response = await axios.get<UserResponse>('https://jsonplaceholder.typicode.com/users');
// 接口返回?cái)?shù)據(jù)符合UserResponse類型,data為User數(shù)組
setUsers(response.data.data);
setError(null);
} catch (err) {
setError('請(qǐng)求用戶列表失敗,請(qǐng)稍后再試');
console.error('請(qǐng)求錯(cuò)誤:', err);
} finally {
setLoading(false);
}
};
fetchUsers();
}, []);
// 加載中
if (loading) return <div style={{ textAlign: 'center', padding: 50 }}>加載中...</div>;
// 錯(cuò)誤提示
if (error) return <div style={{ textAlign: 'center', padding: 50, color: 'red' }}>{error}</div>;
// 渲染用戶列表
return (
<div style={{ maxWidth: 800, margin: '0 auto', padding: 20 }}>
<h2>用戶列表(React+TS+接口請(qǐng)求)</h2>
<table border="1" style={{ width: '100%', borderCollapse: 'collapse', marginTop: 20 }}>
<thead>
<tr style={{ backgroundColor: '#f0f0f0' }}>
<th style={{ padding: 10, textAlign: 'center' }}>ID</th>
<th style={{ padding: 10, textAlign: 'center' }}>姓名</th>
<th style={{ padding: 10, textAlign: 'center' }}>用戶名</th>
<th style={{ padding: 10, textAlign: 'center' }}>郵箱</th>
<th style={{ padding: 10, textAlign: 'center' }}>電話</th>
<th style={{ padding: 10, textAlign: 'center' }}>網(wǎng)站</th>
</tr>
</thead>
<tbody>
{users.map((user) => (
<tr key={user.id}>
<td style={{ padding: 10, textAlign: 'center' }}>{user.id}</td>
<td style={{ padding: 10, textAlign: 'center' }}>{user.name}</td>
<td style={{ padding: 10, textAlign: 'center' }}>{user.username}</td>
<td style={{ padding: 10, textAlign: 'center' }}>{user.email}</td>
<td style={{ padding: 10, textAlign: 'center' }}>{user.phone}</td>
<td style={{ padding: 10, textAlign: 'center' }}>
<a href={`http://${user.website}`} target="_blank" rel="noopener noreferrer">
{user.website}
</a>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default UserList;
五、常見問(wèn)題及解決方案
- 問(wèn)題1:“Property 'xxx' does not exist on type 'never'”? 解決方案:復(fù)雜狀態(tài)(如數(shù)組、對(duì)象)初始值為空時(shí),TS無(wú)法推斷類型,需手動(dòng)指定泛型(如
useState<User[]>([]))。 - 問(wèn)題2:組件Props報(bào)錯(cuò)“Type 'undefined' is not assignable to type 'xxx'”? 解決方案:檢查Props是否為必填項(xiàng),若為可選項(xiàng)添加
?,或給Props設(shè)置默認(rèn)值。 - 問(wèn)題3:事件對(duì)象e報(bào)錯(cuò)“Property 'target' does not exist on type 'Event'”? 解決方案:指定事件類型(如
React.MouseEvent<HTMLButtonElement>),明確目標(biāo)元素類型。 - 問(wèn)題4:接口請(qǐng)求返回?cái)?shù)據(jù)類型不匹配? 解決方案:定義接口返回類型(如案例2中的UserResponse),axios請(qǐng)求時(shí)指定泛型(
axios.get<UserResponse>(url))。
六、總結(jié)
React+TS的核心是“類型約束”,重點(diǎn)掌握3個(gè)核心點(diǎn):Props類型定義(interface)、State類型推導(dǎo)與手動(dòng)指定、事件類型定義。通過(guò)基礎(chǔ)語(yǔ)法練習(xí)和實(shí)戰(zhàn)案例,能快速適應(yīng)TS在React中的使用,尤其在中大型項(xiàng)目中,TS能顯著提升代碼質(zhì)量和開發(fā)效率。
到此這篇關(guān)于React搭配TypeScript使用教程及實(shí)戰(zhàn)案例的文章就介紹到這了,更多相關(guān)React搭配TypeScript使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- React使用TypeScript的最佳實(shí)踐和技巧
- react+typescript中使用echarts的實(shí)現(xiàn)步驟
- 使用?TypeScript?開發(fā)?React?函數(shù)式組件
- React+TypeScript項(xiàng)目中使用CodeMirror的步驟
- React+umi+typeScript創(chuàng)建項(xiàng)目的過(guò)程
- React+TypeScript進(jìn)行項(xiàng)目構(gòu)建案例講解
- TypeScript在React項(xiàng)目中的使用實(shí)踐總結(jié)
- React項(xiàng)目中應(yīng)用TypeScript的實(shí)現(xiàn)
- React+Typescript項(xiàng)目環(huán)境搭建并使用redux環(huán)境的詳細(xì)過(guò)程
相關(guān)文章
React服務(wù)端渲染和同構(gòu)的實(shí)現(xiàn)
本文主要介紹了React服務(wù)端渲染和同構(gòu)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
學(xué)習(xí)ahooks useRequest并實(shí)現(xiàn)手寫
這篇文章主要為大家介紹了學(xué)習(xí)ahooks useRequest并實(shí)現(xiàn)手寫示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
react+antd4實(shí)現(xiàn)優(yōu)化大批量接口請(qǐng)求
這篇文章主要為大家詳細(xì)介紹了如何使用react hooks + antd4實(shí)現(xiàn)大批量接口請(qǐng)求的前端優(yōu)化,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2024-02-02
React組件中監(jiān)聽函數(shù)獲取不到最新的state問(wèn)題
這篇文章主要介紹了React組件中監(jiān)聽函數(shù)獲取不到最新的state問(wèn)題問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
react native 文字輪播的實(shí)現(xiàn)示例
這篇文章主要介紹了react native 文字輪播的實(shí)現(xiàn)示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
React實(shí)現(xiàn)二級(jí)聯(lián)動(dòng)(左右聯(lián)動(dòng))
這篇文章主要為大家詳細(xì)介紹了React實(shí)現(xiàn)二級(jí)聯(lián)動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09

