1. 项目概述为什么ReactTypeScript是当下前端开发的黄金组合最近在带团队新人发现很多朋友在初始化React项目时面对五花八门的脚手架和配置选项还是会感到迷茫。虽然create-react-app简称CRA已经极大地简化了流程但结合TypeScriptTS后如何高效地搭建一个既健壮又便于长期维护的项目这里面依然有不少门道。今天我就以一个过来人的身份把从零开始使用CRA构建ReactTS项目的全过程以及那些官方文档不会告诉你的“踩坑”经验掰开揉碎了讲清楚。简单来说这个项目就是用Facebook官方维护的create-react-app脚手架快速生成一个集成了TypeScript的React单页应用。它能帮你一键搞定Babel、Webpack、Jest、ESLint等繁琐的工程化配置让你可以立刻专注于业务代码的编写。而TypeScript的加入则为这个项目带来了静态类型检查、更好的代码提示和重构能力显著提升了代码质量和团队协作效率。无论你是刚接触ReactTS的新手还是想优化现有工作流的老手这篇超详细的指南都能给你提供一套可复现、可落地的方案。2. 项目整体设计与思路拆解2.1 技术选型背后的考量为什么是CRA TS在启动一个前端项目时技术选型是第一步也是最关键的一步。我选择CRATS这个组合是基于以下几个核心考量首先是开发效率与学习成本的平衡。对于大多数业务项目尤其是需要快速启动和迭代的从零配置Webpack、Babel、热更新HMR、代码分割等是一笔巨大的时间开销且容易出错。CRA将这些复杂性完全封装提供了“开箱即用”的体验。它内置的配置经过了大量项目的验证稳定且性能优化良好。对于新手可以绕过配置地狱直接学习React和TS本身对于老手则可以快速搭建原型或启动新项目后期如有特殊需求也可以通过eject弹出配置或使用craco、react-app-rewired等工具进行定制。其次是类型安全带来的长期收益。JavaScript的灵活性是一把双刃剑在项目规模增长和多人协作时动态类型容易导致运行时错误和沟通成本增加。TypeScript通过在编译时进行类型检查可以将很多潜在错误扼杀在编码阶段。例如组件Props的类型定义、API接口返回数据的形状、复杂状态对象的结构都可以用TS清晰地约束。这虽然增加了一点前期编写类型定义的时间但极大地减少了后期的调试和维护成本特别适合中大型项目。最后是生态与未来的兼容性。CRA是React官方推荐的脚手架其更新与React核心版本保持同步确保了最佳兼容性。TypeScript也已成为前端社区的主流选择绝大多数流行的React库如React Router, Redux Toolkit, Ant Design都提供了高质量的TypeScript类型定义文件*.d.ts集成体验非常顺畅。选择这个组合意味着你站在了社区最佳实践的肩膀上。2.2 项目结构与配置预设使用CRA创建的项目其目录结构是经过精心设计的理解这个结构有助于我们更好地组织代码。一个标准的CRATS项目初始化后的结构大致如下my-react-ts-app/ ├── node_modules/ ├── public/ │ ├── index.html # 页面模板 │ ├── favicon.ico │ └── manifest.json # PWA应用清单 ├── src/ │ ├── App.css # 根组件样式 │ ├── App.test.tsx # 根组件测试可选 │ ├── App.tsx # 根组件 │ ├── index.css # 全局样式 │ ├── index.tsx # 应用入口文件 │ ├── logo.svg │ ├── react-app-env.d.ts # CRA提供的TS类型声明 │ └── setupTests.ts # 测试文件初始化配置 ├── .gitignore ├── package.json # 项目依赖和脚本 ├── tsconfig.json # TypeScript配置文件 └── README.md在动手之前我们需要明确一些预设的配置思路。tsconfig.json文件是TypeScript编译的核心CRA会为我们生成一个优化的默认配置。通常我们不需要在初期大幅修改它但有几个关键选项需要了解strict: true启用所有严格的类型检查选项这是保证代码质量的关键建议始终开启。esModuleInterop: true允许我们使用更符合ES6规范的默认导入方式如import React from react而不是import * as React from react。lib: [dom, dom.iterable, esnext]包含了浏览器环境DOM和最新ES特性的类型定义。include: [src]指定TypeScript只处理src目录下的文件避免编译node_modules等无关目录。我们的设计思路是利用CRA搭建稳固的底层工程地基利用TypeScript构建清晰可靠的代码上层建筑并通过合理的目录结构约定为项目的可维护性和可扩展性打下基础。3. 核心细节解析与实操要点3.1 环境准备与项目创建在敲下第一条命令之前确保你的本地开发环境已经就绪。你需要安装Node.js和npm或yarn、pnpm。我强烈建议使用Node的长期支持版本比如当前的18.x或20.x以获得更好的稳定性和兼容性。你可以通过终端命令node -v和npm -v来验证安装。注意尽量避免使用操作系统自带的包管理器如apt安装Node.js版本可能过旧且管理混乱。推荐从Node.js官网直接下载安装包或使用nvmMac/Linux或nvm-windows进行版本管理这在需要切换不同项目Node版本时非常方便。接下来是创建项目。打开你的终端进入你打算存放项目的目录运行以下命令npx create-react-app my-react-ts-app --template typescript这条命令是整个过程的核心npx一个npm包执行工具它允许你直接运行远程npm包中的命令而无需先全局安装create-react-app。这能保证你每次使用的都是最新版本的脚手架。create-react-app脚手架工具本身。my-react-ts-app这是你的项目名称可以根据需要修改。注意名称中不能包含大写字母只能使用小写字母、数字和连字符。--template typescript这是关键参数它告诉CRA使用TypeScript模板来初始化项目。如果你省略了这个参数创建的就是一个普通的JavaScript项目后续再手动添加TS配置会麻烦得多。命令执行后你会看到终端开始忙碌地下载依赖、配置项目。这个过程可能需要几分钟取决于你的网络速度。完成后进入项目目录并启动开发服务器cd my-react-ts-app npm start如果一切顺利你的默认浏览器会自动打开http://localhost:3000并显示那个经典的React旋转图标页面。恭喜你的ReactTypeScript项目已经成功跑起来了3.2 TypeScript集成深度解析你可能会有疑问CRA是怎么把TypeScript集成进来的我们并没有手动安装typescript包。奥秘就在于--template typescript这个参数。CRA的TypeScript模板内部已经预置了所有必要的依赖。打开package.json文件你会看到在dependencies或devDependencies中已经包含了typescript、types/react、types/react-dom等关键包。types/开头的包是社区维护的TypeScript类型定义文件它们为对应的JavaScript库如react提供了完整的类型描述使得我们在编写代码时能获得智能提示和类型检查。一个至关重要的文件是src/react-app-env.d.ts。它的内容通常只有一行/// reference typesreact-scripts /这行三斜线指令是一个类型声明指令它告诉TypeScript编译器去引入react-scripts包CRA的核心中定义的类型。这使我们能够直接导入图片、CSS模块等资源而不会出现“找不到模块”的类型错误。例如你可以写import logo from ./logo.svg;TypeScript会知道logo是一个字符串类型的路径这背后就是react-scripts提供的类型声明在起作用。关于tsconfig.json的定制虽然CRA生成的默认配置已经很好但随着项目发展你可能需要调整。例如如果你想配置路径别名指向src目录来简化导入语句可以修改tsconfig.json{ compilerOptions: { // ... 其他配置保持不变 baseUrl: src, paths: { /*: [./*] } } }这样你就可以用import Button from /components/Button;代替import Button from ../../components/Button;。但请注意这仅解决了TypeScript的类型解析要让Webpack也能正确识别这个别名你还需要额外的配置例如使用craco因为CRA默认不支持在tsconfig.json中自定义paths。4. 实操过程与核心环节实现4.1 编写第一个类型安全的React组件让我们动手创建一个简单的计数器组件来体验TypeScript带来的类型安全。在src目录下新建一个文件components/Counter.tsx。首先我们定义这个组件的Props类型。计数器可能需要一个初始值以及一个可选的步进值。// Counter.tsx import React, { useState } from react; // 1. 定义组件的Props接口 interface CounterProps { initialValue?: number; // 初始值可选默认为0 step?: number; // 每次增加的步长可选默认为1 }接下来我们使用React的函数组件范式FC来编写组件。这里有一个细节我们使用React.FCCounterProps这个泛型类型来定义组件。它明确了该组件的props类型是CounterProps并且会自动为组件提供childrenprop的类型即使我们这里没用到。// 2. 使用React.FC泛型定义组件 const Counter: React.FCCounterProps ({ initialValue 0, step 1 }) { // 3. 使用useState Hook并显式指定状态类型为number const [count, setCount] useStatenumber(initialValue); const increment () setCount(prevCount prevCount step); const decrement () setCount(prevCount prevCount - step); const reset () setCount(initialValue); return ( div style{{ padding: 20px, border: 1px solid #ccc }} h2当前计数: {count}/h2 button onClick{increment}{step}/button button onClick{decrement} style{{ margin: 0 10px }}-{step}/button button onClick{reset}重置/button p初始值: {initialValue}, 步长: {step}/p /div ); }; export default Counter;现在在App.tsx中使用这个组件// App.tsx import React from react; import ./App.css; import Counter from ./components/Counter; // 如果配置了路径别名这里会更简洁 function App() { return ( div classNameApp header classNameApp-header Counter initialValue{10} step{2} / {/* 尝试传递错误类型的值TS会立即报错 */} {/* Counter initialValuehello / */} /header /div ); } export default App;实操心得始终为useState提供明确的泛型类型。虽然TS通常能根据初始值推断类型但遇到null初始值或复杂对象时显式声明可以避免后续的类型问题。例如useStateUser | null(null)。React.FC的争议与替代方案社区中有一部分声音不推荐使用React.FC因为它隐式包含了children属性即使你的组件并不需要。另一种更简洁的做法是直接给函数和参数声明类型const Counter ({ initialValue 0, step 1 }: CounterProps) { ... };这两种方式都可以团队内部保持一致即可。我个人在明确不需要children的组件中倾向于使用后一种。4.2 状态管理与事件处理的类型定义随着组件复杂度的提升状态和事件处理函数的类型定义变得尤为重要。让我们创建一个简单的待办事项列表组件。// components/TodoList.tsx import React, { useState, ChangeEvent, FormEvent } from react; // 定义单个待办事项的类型 interface TodoItem { id: number; text: string; completed: boolean; } const TodoList: React.FC () { // 状态待办事项列表类型是TodoItem数组 const [todos, setTodos] useStateTodoItem[]([ { id: 1, text: 学习TypeScript, completed: true }, { id: 2, text: 构建React项目, completed: false }, ]); // 状态输入框的当前值类型是字符串 const [inputValue, setInputValue] useStatestring(); // 事件处理输入框变化。使用React提供的ChangeEvent泛型类型指定是HTMLInputElement上的事件 const handleInputChange (e: ChangeEventHTMLInputElement) { setInputValue(e.target.value); // e.target.value 被推断为string }; // 事件处理表单提交。同样使用FormEvent泛型 const handleSubmit (e: FormEventHTMLFormElement) { e.preventDefault(); if (!inputValue.trim()) return; const newTodo: TodoItem { id: Date.now(), // 简单用时间戳作为id text: inputValue, completed: false, }; setTodos(prevTodos [...prevTodos, newTodo]); setInputValue(); // 清空输入框 }; // 事件处理切换完成状态。这里我们不需要事件对象所以参数是id const toggleTodo (id: number) { setTodos(prevTodos prevTodos.map(todo todo.id id ? { ...todo, completed: !todo.completed } : todo ) ); }; return ( div form onSubmit{handleSubmit} input typetext value{inputValue} onChange{handleInputChange} placeholder添加新任务... / button typesubmit添加/button /form ul {todos.map(todo ( li key{todo.id} style{{ textDecoration: todo.completed ? line-through : none }} onClick{() toggleTodo(todo.id)} // 点击切换状态 {todo.text} /li ))} /ul /div ); }; export default TodoList;关键点解析ChangeEventHTMLInputElement这是一个泛型类型来自types/react。它精确地描述了发生在input元素上的变更事件。当你输入e.target.时编辑器会智能提示出value、checked、files等属于input元素的属性而不是通用的事件目标属性。这极大地提升了开发体验和代码安全性。FormEventHTMLFormElement同理用于表单提交事件。状态数组的更新在setTodos中我们使用了函数式更新prevTodos ...这是React推荐的做法尤其是在新状态依赖于旧状态时。TypeScript能很好地推断出prevTodos的类型就是TodoItem[]。4.3 样式方案与资源导入的类型支持CRA支持多种样式方案包括普通的CSS、Sass、CSS Modules等。对于TypeScript项目使用CSS Modules可以获得最佳的类型支持。首先安装Sass如果你需要使用Sass语法npm install sass # 或 yarn add sass然后创建一个使用CSS Modules的组件。将组件样式文件命名为[name].module.css或[name].module.scss。// components/Button/Button.tsx import React from react; import styles from ./Button.module.scss; // 导入CSS Module interface ButtonProps { variant?: primary | secondary; onClick?: () void; children: React.ReactNode; } const Button: React.FCButtonProps ({ variant primary, onClick, children }) { // 根据variant动态组合class名 const buttonClass ${styles.button} ${styles[variant]}; return ( button className{buttonClass} onClick{onClick} {children} /button ); }; export default Button;对应的样式文件// Button.module.scss .button { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; .primary { background-color: #007bff; color: white; :hover { background-color: #0056b3; } } .secondary { background-color: #6c757d; color: white; :hover { background-color: #545b62; } } }这里的神奇之处在于TypeScript对CSS Modules的支持。当你导入一个.module.scss文件时TypeScript配合CRA的配置会将它识别为一个对象这个对象的键如button、primary都是字符串类型。这意味着如果你在styles后面输入.编辑器会给出所有可用的类名提示并且如果你拼写错了类名如styles.primryTypeScript会在编译时报错避免了因样式类名拼写错误导致的bug。对于图片、字体等静态资源直接导入即可TypeScript也能正确识别其类型为字符串通常是路径或特定的模块。import logo from /assets/logo.png; import { ReactComponent as ReactLogo } from /assets/logo.svg; // 对于SVG可以作为React组件导入 const MyComponent () ( div img src{logo} altLogo / ReactLogo width{50} height{50} / /div );5. 常见问题与排查技巧实录在实际开发中你一定会遇到各种类型错误和配置问题。下面是我总结的一些高频问题及其解决方案。5.1 类型错误排查指南问题1Property X does not exist on type Y这是最常见的错误通常发生在你尝试访问一个对象上不存在的属性时。场景你从API获取了一个用户数据user然后尝试访问user.nmae正确应为user.name。排查首先检查拼写。确认你访问的对象类型定义是否正确。去查看定义该对象的接口或类型别名。如果数据来自外部API确保你为API响应正确定义了类型。可以使用在线工具如quicktype.io根据JSON响应生成TypeScript接口。问题2Type A is not assignable to type B类型不匹配错误。场景将一个字符串赋值给一个期望是数字的状态。排查检查变量声明时的类型和实际赋值是否一致。对于函数参数检查传入的实参类型是否符合形参的类型定义。对于useState检查初始值类型是否与泛型参数匹配。例如useStatenumber(hello)就会报错。问题3Cannot find module ./X.module.css or its corresponding type declarations找不到模块或其类型声明。排查首先确认文件路径是否正确。对于CSS Modules确保文件名格式是*.module.css或*.module.scss。这是CRA识别CSS Modules的约定。如果问题依旧尝试重启TypeScript语言服务器。在VSCode中可以按CtrlShiftP然后输入 “Restart TS Server”。5.2 第三方库集成类型问题问题使用没有内置类型定义的第三方库有些较老的或小众的JavaScript库可能没有自带TypeScript类型定义types/包。解决方案1安装社区类型包首先尝试搜索types/库名。例如如果使用lodash就运行npm install --save-dev types/lodash。解决方案2声明模块如果找不到types包你需要在项目中自己声明。在src目录下创建一个类型声明文件例如custom.d.ts// custom.d.ts declare module untyped-library { const lib: any; // 先用any类型绕过检查但尽量定义具体类型 export default lib; }或者对于没有默认导出的库declare module untyped-library { export function someFunction(arg: string): void; export const someConstant: number; }解决方案3使用require或动态导入作为临时方案你可以使用CommonJS的require语法或者使用import()动态导入TypeScript对它们的检查会宽松一些但这不是推荐做法。问题使用Ant Design等UI库的按需加载与类型为了优化打包体积我们常配合babel-plugin-import进行按需加载。但在TypeScript中这可能导致类型错误因为TS编译器找不到从antd直接导入子组件如import { Button } from antd;的类型。解决方案确保你的tsconfig.json中设置了moduleResolution: nodeCRA默认已设置。通常只要正确安装了types/库名类型就能正常工作。如果仍有问题检查库的官方文档看是否有特殊的TypeScript配置说明。5.3 构建与部署优化构建速度优化随着项目增长TypeScript的类型检查可能会拖慢开发服务器的启动速度。技巧在开发时可以考虑使用--transpile-only标志来跳过类型检查仅进行转译。但这需要修改启动脚本且不推荐长期使用因为会失去类型检查的保护。一个更好的实践是确保tsconfig.json中的include字段只包含源代码目录排除node_modules和构建输出目录。生产环境构建运行npm run build会创建一个经过压缩和优化的build文件夹。CRA的TypeScript模板已经配置好了生产构建流程它会使用tsc进行类型检查如果类型错误构建会失败。使用Babel将TSX/TS代码转译为浏览器兼容的JavaScript。使用Webpack进行打包、代码分割、压缩等优化。部署注意事项路由与SPA如果你使用了React Router等客户端路由在部署到像Nginx、Apache这样的静态服务器时需要配置将所有路径的请求重定向到index.html否则刷新非根路径的页面会出现404错误。环境变量CRA使用REACT_APP_开头的环境变量。在构建时它们会被嵌入到静态文件中。确保敏感信息如API密钥不要直接放在前端代码中应通过后端服务中转或使用安全的配置管理方式。5.4 进阶配置与Eject决策何时考虑弹出配置EjectCRA提供了一个npm run eject命令它会将所有的构建配置Webpack, Babel, ESLint等“弹出”到你的项目目录中让你获得完全的控制权。这是一个单向操作不可逆何时需要弹出当你需要深度定制构建流程而CRA默认配置和社区工具如craco都无法满足时。例如需要添加一个特殊的Webpack loader或者修改Babel插件链的核心顺序。强烈建议在eject之前先尝试使用craco或react-app-rewired这类覆盖配置的工具。它们允许你修改Webpack和Babel配置而无需eject风险小得多。使用CRACO进行自定义配置示例假设我们想添加路径别名并让它同时被TypeScript和Webpack识别。安装CRACOnpm install craco/craco --save-dev在项目根目录创建craco.config.jsconst path require(path); module.exports { webpack: { alias: { : path.resolve(__dirname, src), }, }, };修改package.json中的脚本用craco替换react-scriptsscripts: { start: craco start, build: craco build, test: craco test, eject: react-scripts eject }同时确保tsconfig.json中也配置了路径映射如前文所述。这样路径别名就能在开发和构建中都正常工作了。从我的经验来看对于90%的项目CRA的默认配置加上CRACO这样的工具进行微调已经完全足够。只有当你遇到非常特殊的构建需求时才需要慎重考虑eject。一旦eject你就需要自己负责维护整个复杂的构建配置升级React版本也会变得更具挑战性。6. 项目结构与代码组织最佳实践一个清晰的项目结构是长期可维护性的基石。随着项目功能增加src目录很容易变得混乱。以下是我在多个项目中总结出的一种可扩展的结构供你参考src/ ├── assets/ # 静态资源 (图片、字体、样式文件) │ ├── images/ │ ├── fonts/ │ └── styles/ # 全局样式、变量、mixin ├── components/ # 通用UI组件 (可复用的、无业务逻辑的) │ ├── Button/ │ │ ├── Button.tsx │ │ ├── Button.module.scss │ │ └── index.ts # 导出组件简化导入 │ ├── Input/ │ └── index.ts # 统一导出所有通用组件 ├── features/ # 按功能/业务模块组织 (推荐) │ ├── auth/ # 认证相关 │ │ ├── components/ # 该功能特有的组件 │ │ ├── hooks/ # 该功能特有的自定义Hook │ │ ├── types/ # 该功能特有的类型定义 │ │ ├── utils/ # 该功能特有的工具函数 │ │ └── index.ts # 功能模块入口 │ └── dashboard/ # 仪表盘相关 ├── hooks/ # 全局可复用的自定义Hook (如useLocalStorage) ├── lib/ # 第三方库的封装或初始化代码 ├── pages/ # 页面级组件 (与路由一一对应) ├── services/ # API请求层封装所有与后端的交互 │ ├── apiClient.ts # Axios实例配置 │ └── userService.ts # 用户相关API ├── stores/ # 状态管理 (如Zustand、Redux slices) ├── types/ # 全局共享的类型定义 ├── utils/ # 全局工具函数 ├── App.tsx ├── index.tsx └── react-app-env.d.ts关键实践使用index.ts文件简化导入在每个组件或功能目录下放一个index.ts内容为export { default } from ./Button;或export * from ./types;。这样在其他地方导入时可以直接写import Button from /components/Button;而不是import Button from /components/Button/Button;。功能文件夹Feature Folders将UI、逻辑、类型、API调用等按功能而非技术角色组织在一起。这符合“高内聚、低耦合”的原则让与某个业务功能相关的所有代码都在一起便于理解和修改。类型定义就近存放组件自己的Props和State类型可以直接定义在组件文件内。如果类型被多个地方共享则提升到父级目录的types文件夹中甚至到全局的src/types目录下。7. 测试与代码质量保障CRA默认集成了Jest作为测试框架并配置好了对TypeScript和React组件的测试支持。7.1 编写类型安全的单元测试为之前创建的Counter组件编写一个测试。首先安装React测试库如果尚未安装npm install --save-dev testing-library/react testing-library/jest-dom testing-library/user-event然后创建测试文件Counter.test.tsx// Counter.test.tsx import React from react; import { render, screen, fireEvent } from testing-library/react; import testing-library/jest-dom/extend-expect; // 添加额外的断言如 toBeInTheDocument import Counter from ./Counter; describe(Counter Component, () { test(renders with default initial value (0), () { render(Counter /); // 使用 getByText 并配合正则表达式来查找包含“当前计数: 0”的元素 expect(screen.getByText(/当前计数: 0/)).toBeInTheDocument(); }); test(renders with custom initial value and step, () { render(Counter initialValue{10} step{2} /); expect(screen.getByText(/当前计数: 10/)).toBeInTheDocument(); expect(screen.getByText(/步长: 2/)).toBeInTheDocument(); }); test(increments counter by step when button is clicked, () { render(Counter initialValue{5} step{3} /); const incrementButton screen.getByText(3); fireEvent.click(incrementButton); expect(screen.getByText(/当前计数: 8/)).toBeInTheDocument(); }); test(decrements counter by step when - button is clicked, () { render(Counter initialValue{5} step{2} /); const decrementButton screen.getByText(-2); fireEvent.click(decrementButton); expect(screen.getByText(/当前计数: 3/)).toBeInTheDocument(); }); test(resets counter to initial value when reset button is clicked, () { render(Counter initialValue{100} /); const incrementButton screen.getByText(1); const resetButton screen.getByText(重置); fireEvent.click(incrementButton); // 变成101 fireEvent.click(resetButton); // 重置回100 expect(screen.getByText(/当前计数: 100/)).toBeInTheDocument(); }); });运行测试npm test。Jest会进入监听模式当你修改测试或源码时自动重新运行相关测试。TypeScript在测试中的优势在编写测试时你同样能获得完整的类型提示。例如render(Counter /)会要求你传入符合CounterProps类型的参数。如果你传错了类型比如initialValuehelloTypeScript编译器会在你写代码的时候就报错而不是等到运行时测试失败才发现。7.2 集成ESLint与PrettierCRA已经内置了ESLint配置。对于TypeScript项目我们主要关注typescript-eslint相关的规则。你可以在项目根目录的.eslintrc.js如果没有可以创建或package.json的eslintConfig字段中进行扩展。一个常见的配置扩展是同时集成Prettier进行代码格式化并解决两者之间的规则冲突。安装依赖npm install --save-dev eslint-config-prettier eslint-plugin-prettier prettier创建或修改.eslintrc.jsmodule.exports { extends: [ react-app, // CRA的基础配置 react-app/jest, plugin:typescript-eslint/recommended, // TS推荐规则 prettier, // 禁用ESLint中与Prettier冲突的规则必须放在最后 ], plugins: [prettier], rules: { prettier/prettier: error, // 将Prettier的格式化问题标记为错误 typescript-eslint/no-unused-vars: [warn, { argsIgnorePattern: ^_ }], // 允许以下划线开头的参数未使用 // 可以在此添加或覆盖其他规则 }, };创建.prettierrc{ semi: true, trailingComma: es5, singleQuote: true, printWidth: 100, tabWidth: 2 }在package.json中添加格式化脚本scripts: { lint: eslint src --ext ts,tsx --fix, format: prettier --write \src/**/*.{ts,tsx,css,scss,json}\ }现在你可以运行npm run lint来检查和自动修复ESLint问题运行npm run format来格式化代码。更高效的做法是在编辑器中配置保存时自动格式化。这套组合拳能确保你的代码风格一致并提前捕获许多潜在的错误和不良实践是保障大型项目代码质量的必备工具。