# react-ts-project **Repository Path**: kayanchan/react-ts-project ## Basic Information - **Project Name**: react-ts-project - **Description**: Typescript in React ( https://scrimba.com/learn-typescript-c03c ) 课程实践 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-10-23 - **Last Updated**: 2025-11-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # react-ts-project ## [Typescript in React](https://scrimba.com/learn-typescript-c03c) ## Setting up 安装 1. vite脚手架创建项目 ```bash npm create vite # Ok to proceed? (Yes) # Project name: . # Select a framework: React # Select a variant: TypeScript npm i # clsx: 用于条件性构建 className 字符串的工具库 # react-confetti: 一个用于创建五彩纸屑庆祝效果的 React 组件 npm i clsx react-confetti ``` ## Basic & Custom Types 1. words.ts 2. languages.ts ## Functions 1. utils.ts ## A new getRandomIndex() function ## useState ```javascript const [currentWord, setCurrentWord] = useState(():string => getRandomWord()) const [guessedLetters, setGuessedLetters] = useState([]) ``` ## Derived Values and Arrow Functions ```javascript const numGuessesLeft: number = languages.length - 1 const wrongGuessCount: number = guessedLetters.filter((letter:string):boolean => !currentWord.includes(letter)).length const isGameWon: boolean = currentWord.split("").every((letter:string):boolean => guessedLetters.includes(letter)) const isGameLost: boolean = wrongGuessCount >= numGuessesLeft const isGameOver: boolean = isGameWon || isGameLost const lastGuessedLetter: string = guessedLetters[guessedLetters.length - 1] const isLastGuessIncorrect: boolean = !!lastGuessedLetter && !currentWord.includes(lastGuessedLetter) ``` ## App.jsx Functions ```javascript function addGuessedLetter(letter: string): void { setGuessedLetters((prevLetters: string[]): string[] => prevLetters.includes(letter) ? prevLetters : [...prevLetters, letter] ) } function startNewGame():void { setCurrentWord(getRandomWord()) setGuessedLetters([]) } ``` ## React Components ### 函数声明+显式类型注解 - 使用 function 声明 - 显式指定返回类型为 JSX.Element - 不需要额外的 React 类型导入(除了 JSX) - 不自动包含 children props ```javascript import type {JSX} from 'react' export default function Header(): JSX.Element { return (

Assembly: Endgame

Guess the word within 8 attempts to keep the programming world safe from Assembly!

) } // props interface HeaderProps { title: string; subtitle?: string; } export default function Header({ title, subtitle }: HeaderProps): JSX.Element { // ... } ``` ### React.FC 写法 - 使用箭头函数 - 使用 React.FC 类型(需要导入 React) - 自动包含 children props(即使没有明确定义) - 更符合 React 函数组件的官方类型定义 ```javascript const Header: React.FC = () => { return (

Assembly: Endgame

Guess the word within 8 attempts to keep the programming world safe from Assembly!

); }; export default Header; ``` ## JSX Elements or null ```javascript import type { JSX } from "react"; import Confetti from "react-confetti"; export default function ConfettiContainer({ isGameWon, }: { isGameWon: boolean; }): JSX.Element | null { if (!isGameWon) { return null; } else { return ; } } ``` ## Custom Component Prop Types ```javascript import clsx from "clsx"; import { getFarewellText } from "../utils"; import { languages } from "../languages"; import type { JSX } from "react"; type GameStatusProps = { isGameWon: boolean; isGameLost: boolean; isGameOver: boolean; isLastGuessIncorrect: boolean; wrongGuessCount: number; }; export default function GameStatus({ isGameWon, isGameLost, isGameOver, isLastGuessIncorrect, wrongGuessCount, }: GameStatusProps): JSX.Element { const gameStatusClass:string = clsx("game-status", { won: isGameWon, lost: isGameLost, farewell: !isGameOver && isLastGuessIncorrect, }); return (
{!isGameOver && isLastGuessIncorrect && (

{getFarewellText(languages[wrongGuessCount - 1].name)}

)} {isGameWon && ( <>

You win!

Well done! 🎉

)} {isGameLost && ( <>

Game over!

You lose! Better start learning Assembly 😭

)} {/* If none of the above conditions met, render nothing inside but keep the section */}
); } ``` ## Component Props and Imported Types ```javascript export type Language = { name: string; backgroundColor: string; color: string }; ``` ```javascript import { clsx } from "clsx"; import type { JSX } from "react"; import type { Language } from "../languages"; type LanguageChipsProps = { languages: Language[]; wrongGuessCount: number; }; export default function LanguageChips({ languages, wrongGuessCount, }: LanguageChipsProps): JSX.Element { const languageElements = languages.map((lang: Language, index: number): JSX.Element => { const isLanguageLost = index < wrongGuessCount; const styles = { backgroundColor: lang.backgroundColor, color: lang.color, }; const className = clsx("chip", isLanguageLost && "lost"); return ( {lang.name} ); }); return
{languageElements}
; } ``` ## Element within an Element ```javascript import { clsx } from "clsx"; import type { JSX } from "react"; import type { Language } from "../languages"; type LanguageChipsProps = { languages: Language[]; wrongGuessCount: number; }; export default function LanguageChips({ languages, wrongGuessCount, }: LanguageChipsProps): JSX.Element { const languageElements: JSX.Element[] = languages.map((lang: Language, index: number): JSX.Element => { const isLanguageLost: boolean = index < wrongGuessCount; // Omit 工具类型 // 从对象类型中排除指定的属性 const styles: Omit = { backgroundColor: lang.backgroundColor, color: lang.color, }; const className: string = clsx("chip", isLanguageLost && "lost"); return ( {lang.name} ); }); return
{languageElements}
; } ``` ## Function Props 语法:`props: (parameters) => returnType` ```javascript import type { JSX } from "react"; type NewGameButtonProps = { isGameOver: boolean; startNewGame: () => void; }; export default function NewGameButton({ isGameOver, startNewGame, }: NewGameButtonProps): JSX.Element | null { if (!isGameOver) { return null; } else { return ( ); } } ``` # 内置类型工具 ## Partial - 所有属性设置为可选 ```javascript interface User { name: string age: number address: string } function updateUser(user: User, field: Partial): User { return {...user, ...field} } const user: User = {name: 'xiaoming', age: 18, address: 'beijing'} const user2 = updateUser(user, {name: 'xiaohong'}) console.log(user2) ``` ## Required - 所有属性设置为必填 ```javascript interface Props { name?: string age?: number } function printProps(props: Required): void { console.log(props.name) console.log(props.age) } // Props 接口定义了可选属性:name?: string 和 age?: number,存在属性为undefined的可能 // printProps 函数要求参数是 Required,所有属性必须存在且不能为 undefined // const user3: Props = { name: 'xiaoming', age: 18 } // wrong const user3: Required = { name: 'xiaoming', age: 18 } printProps(user3) ``` ## Readonly - 所有属性设置为只读 ```javascript const user4: Readonly = { name: 'xiaoming', age: 30, address: 'beijing' } // user4.name = 'xiaohong' // 无法为“name”赋值,因为它是只读属性 ``` ## Record - 创建一个对象类型,对象中的属性名是K,属性值是V ```javascript type UserName = 'xiaoming' | 'xiaohong' const user5: Record = { xiaoming: { name: 'xiaoming', age: 18, address: 'beijing' }, xiaohong: { name: 'xiaohong', age: 18, address: 'beijing' } } console.log(user5) ``` ## Pick - 从对象类型中选择指定的属性 ```javascript type NameAndAgeOnly = Pick const user6: NameAndAgeOnly = { name: 'xiaoming', age: 18 } console.log(user6) ``` ## Exclude - 从联合类型中排除指定的属性 ```javascript type UserExcludeAddress = Exclude let key: UserExcludeAddress = 'name' key = 'age' console.log(key) const a = 'a' as const const b = 'b' as const const c = 'c' as const type ABC = typeof a | typeof b | typeof c type AB = Exclude ``` ## Extract - 从联合类型中选择指定的属性 ```javascript type Person = { name: string; age: string; }; const nameKey: Extract = "name" console.log(nameKey) ``` ## Omit - 从对象类型中排除指定的属性 ```javascript type UserOmitAge = Omit; const user8: UserOmitAge = { name: 'xiaoming', address: 'beijing' }; console.log(user8); ``` # React + TypeScript + Vite This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. Currently, two official plugins are available: - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh ## React Compiler The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). ## Expanding the ESLint configuration If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: ```js export default defineConfig([ globalIgnores(['dist']), { files: ['**/*.{ts,tsx}'], extends: [ // Other configs... // Remove tseslint.configs.recommended and replace with this tseslint.configs.recommendedTypeChecked, // Alternatively, use this for stricter rules tseslint.configs.strictTypeChecked, // Optionally, add this for stylistic rules tseslint.configs.stylisticTypeChecked, // Other configs... ], languageOptions: { parserOptions: { project: ['./tsconfig.node.json', './tsconfig.app.json'], tsconfigRootDir: import.meta.dirname, }, // other options... }, }, ]) ``` You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: ```js // eslint.config.js import reactX from 'eslint-plugin-react-x' import reactDom from 'eslint-plugin-react-dom' export default defineConfig([ globalIgnores(['dist']), { files: ['**/*.{ts,tsx}'], extends: [ // Other configs... // Enable lint rules for React reactX.configs['recommended-typescript'], // Enable lint rules for React DOM reactDom.configs.recommended, ], languageOptions: { parserOptions: { project: ['./tsconfig.node.json', './tsconfig.app.json'], tsconfigRootDir: import.meta.dirname, }, // other options... }, }, ]) ``` # 路由:react-router # 状态管理:Mobx # 状态管理:Zustand # 状态管理:Jotai