在 TypeScript 中使用 React 可以提高代碼的可維護性、可讀性和可靠性。TypeScript 提供了靜態類型檢查和豐富的類型系統,這些功能在 React 開發中非常有用。下面詳細介紹如何在 React 項目中使用 TypeScript,并結合泛型和 infer 來定義類型。
1.?項目初始化
首先,創建一個使用 TypeScript 的 React 項目:
pnpm create vite my-app --template react-ts
cd my-app
2.?基本用法
在 TypeScript 中,可以為組件的 props 和 state 定義類型。
函數組件:
import React from 'react';
interface GreetingProps {name: string;
}const Greeting: React.FC<GreetingProps> = (( name )) => {return <h1>Hello, {name}!</h1>;
};export default Greeting;
類組件:
import React, { Component } from 'react';interface CounterProps {initialCount?: number;
}interface CounterState {count: number;
}class Counter extends Component<CounterProps, CounterState> {static defaultProps = {initialCount: 0,};constructor(props: CounterProps) {super(props);this.state = { count: props.initialCount || 0 };render() {return (<div><p>Count: {this.state.count}</p><button onClick={() => this.setState({count: this.state.count + 1})}>Increment</button></div>);}}
}export default Counter;
3. 使用泛型
泛型允許我們定義可以在多種類型之間復用的組件和函數。
要想定義出靈活的組件類型,泛型和 infer 的使用必不可少,如果你在?React 開發過程中大量用到 any ,說明你你沒有真正掌握 Typescript,因為只要你靈活掌握了 Typescript,那么在項目中所有的類型均能通過類型定義來推導約束。
示例:泛型列表組件
import React from 'react';interface ListProps<T> {items: T[];renderItem: (item: T) => React.ReactNode;
}function List<T>({ items, renderItem }: ListProps<T>) {return (<ul>{items.map((item, index) => (<li key={index}>{renderItem(item)}</li>))}</ul>);
}// 使用泛型列表組件
const NumberList = () => {const numbers = [1, 2, 3, 4, 5];return <List items={numbers} renderItem={(num) => <span>{num}</span>} />;
};const StringList = () => {const strings = ['one', 'two', 'three'];return <List items={strings} renderItem={(str) => <strong>{str}</strong>} />;
};export { NumberList, StringList };
4.?使用 infer 定義類型
infer 關鍵字可以在條件類型中使用,用于推斷類型。
示例:推斷函數返回類型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;function getUser() {return { name: 'John', age: 30 };
}type User = GetReturnType<typeof getUser>; // { name: string; age: number; }
示例:推斷組件 props 類型
import React, { ComponentType } from 'react';type InferProps<T> = T extends ComponentType<infer P> ? P : never;const MyComponent = (props: { name: string; age: number }) => {return <div>{props.name} is {props.age} years old.</div>;
};type MyComponentProps = InferProps<typeof MyComponent>; // { name: string; age: number }
5. 綜合示例
綜合示例:使用 TypeScript、泛型和 infer 定義類型并在 React 組件中使用。
import React from 'react';// 定義一個數據類型
interface User {id: number;name: string;
}// 定義一個泛型函數組件,接收數據和渲染函數作為參數
interface DataListProps<T> {data: T[];renderItem: (item: T) => React.ReactNode;
}const DataList = <T,>({ data, renderItem }: DataListProps<T>) => {return (<ul>{data.map((item) => (<li key={(item as any).id}>{renderItem(item)}</li>))}</ul>);
};// 使用 infer 推斷組件 props 類型
type InferProps<T> = T extends React.ComponentType<infer P> ? P : never;// 創建一個渲染 user 數據的組件
const UserList: React.FC<DataListProps<User>> = (props) => {return <DataList {...props} />;
};// 使用 UserList 組件
const App: React.FC = () => {const users: User[] = [{ id: 1, name: 'John' },{ id: 2, name: 'Jane' },];return (<div><h1>User List</h1><UserList data={users} renderItem={(user) => <span>{user.name}</span>} /></div>);
};export default App;
在這個綜合示例中:
1. 定義了一個 User 類型;
2. 創建了一個泛型組件 Datalist,用于渲染任意類型的數據列表;
3. 使用 infer 推斷 UserList 組件的 props 類型;
4. 在 App 組件中使用 UserList 組件渲染用戶數據列表;
6.?針對 React 的進階版 tsconfig
tsconfig.json 是 TypeScript 項目配置的重要文件,通過合理配置,可以提高開發效率和代碼質量。
針對 React 項目,重點配置項包括目標版本、庫、模塊解析、JSX 支持以及嚴格類型檢查選項。
根據項目需求,可以進一步調整和擴展配置,確保最佳的開發體驗。
這是一個基礎的 tsconfig.json 示例,適用于大多數 React 項目:
{"compilerOptions": {"target": "es2017", // 指定 ECMAScript 目標版本。ES5 兼容性最好,可以在大多數瀏覽器中運行"lib": ["dom", "dom.iterable", "esnext"], // 指定編譯時包含的庫文件。通常包括 dom、dom.iterable和esnext"allowJs": true, // 允許編譯 .js 文件。適用于項目中混合使用 TypeScript 和 JavaScript 文件"skipLibCheck": true, // 跳過類型聲明文件的類型檢查。可以加快編譯速度"esModuleInterop": true, // 允許對 ES 模塊默認導入進行編譯時的兼容處理"allowSyntheticDefaultImports": true, // 允許從沒有默認導出的模塊中默認導入。與 esModuleInterop 配合使用"strict": true, // 啟用所有嚴格類型檢查選項。建議開啟以確保類型安全"forceConsistentCasingInFileNames": true, // 禁止文件名大小寫不一致。保證跨平臺一致性"module": "esnext", // 指定模塊代碼生成方式。esnext 適用于現代 JavaScript 運行環境"moduleResolution": "node", // 指定模塊解析策略。node 適用于 Node.js 生態系統"resolveJsonModule": true, // 允許導入 JSON 文件"isolatedModules": true, // 將每個文件作為獨立的模塊。對 Babel 等工具很有用"noEmit": true, // 禁止生成輸出文件。適用于僅進行類型檢查的項目"jsx": "react-jsx", // 指定 JSX 代碼生成方式。React 17 及以上版本推薦使用 react-jsx"baseUrl": "./src", // 配置基地址,使導入模塊時更簡潔"paths": { // 配置路徑別名,使導入模塊時更簡潔"@components/*": ["components/*"],"@utils/*": ["utils/*"]},"strictNullChecks": true, // 啟用嚴格的 null 檢查"noImplicitAny": true, // 禁止隱式的 any 類型"noImplicitThis": true, // 禁止隱式的 this 類型"alwaysStrict": true, // 如終以嚴格模式"noUnusedLocals": true, // 報告未使用的局部變量"noUnusedParameters": true, // 報告未使用的函數參數"noImplicitReturns": true, // 所有代碼路徑必須顯式返回值"noFallthroughCasesInSwitch": true // 禁止 switch 語句中的 case 語句貫穿},"include": ["src"], // 指定要包含的文件和目錄。通常指定src 目錄"exclude": ["node_modules", "build"] // 排除指定的目錄
}