Create React App 添加 TypeScript 完整指南:模板创建、渐进迁移与类型检查链路解析
【免费下载链接】create-react-appSet up a modern web app by running one command.项目地址: https://gitcode.com/gh_mirrors/cr/create-react-app
本篇指南聚焦于在 Create React App(CRA)项目中引入 TypeScript 的两条路径:使用官方typescript模板创建全新项目,以及为现有项目渐进式添加 TypeScript 支持。文中将结合本仓库中react-scripts的源码实现(如 tsconfig 自动生成逻辑、webpack 类型检查插件接入)展开讲解,读完你可以独立完成 TypeScript 项目的初始化、存量项目迁移,并理解 CRA 底层是如何校验和兜底你的 TypeScript 配置的。
功能可用性与前置说明
在开始之前需要明确:本文所述特性自react-scripts@2.1.0及更高版本起可用。TypeScript 是 JavaScript 的类型化超集,最终会被编译为普通 JavaScript 运行,因此你可以在享受静态类型检查的同时,保持浏览器端零额外运行时负担。
另外,从 CRA 4 时代起,全局安装的create-react-app不再受支持。如果你此前通过npm install -g create-react-app安装过全局版本,建议先卸载,以确保npx始终拉取最新版本:
npm uninstall -g create-react-app # 或 yarn global remove create-react-app这一要求在 packages/create-react-app/createReactApp.js 的 CLI 提示中也有对应体现——当项目创建流程因缺少模板中断时,CLI 会直接建议用户卸载全局包后重试。
方式一:用官方 TypeScript 模板创建全新项目
创建带 TypeScript 的 CRA 项目,最简单的方式是在创建命令后追加--template typescript:
npx create-react-app my-app --template typescript使用 Yarn 时等价命令为:
yarn create react-app my-app --template typescript--template参数的使用规则与自定义模板一致:模板在 npm 上的命名格式为cra-template-[template-name],但命令中只需提供[template-name]部分。CRA 的 CLI 在 createReactApp.js 中会解析该参数并解析对应的模板包,例如cra-template-typescript。模板来源不限于 npm 官方包,也可以是本地相对路径、.tgz或.tar.gz归档,详见 自定义模板文档。
官方 TypeScript 模板包含什么
本仓库中的 cra-template-typescript 就是官方提供的 TypeScript 基础模板。创建项目时,init.js 会把template目录下的文件拷贝到项目根目录,并按 template.json 中声明的package.dependencies安装额外依赖,随后移除模板包本身。
模板文件结构(src目录下)包括:
index.tsx:应用入口,使用createRoot挂载 React 应用App.tsx:根组件App.test.tsx:配套测试用例reportWebVitals.ts:性能指标上报入口setupTests.ts:测试环境初始化index.css/App.css:样式文件
模板声明的核心依赖(见 template.json)包括:
| 依赖 | 说明 |
|---|---|
typescript | TypeScript 编译器本体 |
@types/react/@types/react-dom | React 与 React DOM 的类型声明 |
@types/node | Node 环境类型声明(覆盖process、path等) |
@types/jest | Jest 测试框架类型声明 |
@testing-library/react等 | 测试工具链 |
同时,模板在eslintConfig中声明extends: ["react-app", "react-app/jest"],使 ESLint 规则与 TypeScript 解析器配置开箱即用。
项目创建成功后,npm start/npm test/npm run build等脚本的行为与普通 CRA 项目一致(详见 可用脚本文档),区别仅在于源码文件扩展名为.tsx/.ts,且构建过程中多了类型检查环节。
方式二:为现有 CRA 项目渐进添加 TypeScript
对于已经存在的 JavaScript 项目,可以按以下步骤逐步引入 TypeScript,而不必重新创建项目。
第一步:安装 TypeScript 与类型声明
在项目根目录执行:
npm install --save typescript @types/node @types/react @types/react-dom @types/jest使用 Yarn 时:
yarn add typescript @types/node @types/react @types/react-dom @types/jest各类型声明包的用途与上文模板依赖一致:@types/react与@types/react-dom提供 React 组件、Hooks、事件对象等 API 的类型;@types/node覆盖 Node 全局对象与模块;@types/jest让测试文件中的describe、it、expect等全局函数具备类型。若你使用了其他第三方库(如react-router、axios),通常还需要额外安装对应的@types/*包或确认库自带类型声明。
第二步:将源文件重命名为 TypeScript 文件
把入口文件src/index.js重命名为src/index.tsx。只要文件内含 JSX 语法,扩展名就必须是.tsx;纯逻辑文件可以使用.ts。重命名后,React 组件、工具函数可以按需逐个迁移,未迁移的.js文件在allowJs开启的情况下可以继续共存,从而实现渐进式改造。
第三步:确保项目根目录存在 tsconfig.json
重命名文件后,检查项目根目录是否存在tsconfig.json。如果不存在,CRA 会在你下次运行开发服务器时自动为你生成一份(详见下文"tsconfig.json 自动生成机制"一节);当然你也可以参照 TypeScript 官方 tsconfig 文档 手动创建。官方推荐直接编辑自动生成的配置,因为 CRA 会保留你的自定义项,只对缺失或冲突的选项进行修正。
第四步:重启开发服务器
最后,务必重启你的开发服务器(npm start)。这是因为 webpack 的解析规则、类型检查插件是否启用,都取决于项目启动时tsconfig.json是否存在(见下文源码分析),单纯热重载不会触发配置重新加载。
重启后,类型错误会与构建日志一同显示在同一个终端控制台中。你需要先修复这些类型错误,才能继续开发或执行生产构建——这正是 TypeScript 带来的强约束:类型安全是编译前的硬门槛。
tsconfig.json 自动生成机制与默认值解析
很多开发者好奇 CRA 到底往tsconfig.json里写了什么。答案在 verifyTypeScriptSetup.js 中,该脚本由 init.js 在模板安装完成后(检测到安装依赖中包含typescript时)调用,用于初始化 TypeScript 配置。
自动生成的时机
verifyTypeScriptSetup的核心逻辑如下(verifyTypeScriptSetup.js):
- 若项目根目录不存在
tsconfig.json,且src下检测到.ts/.tsx文件,则先写入一个空对象{}占位,标记为首次初始化; - 若
src下没有任何 TypeScript 文件,则直接返回,不做任何处理; - 若
tsconfig.json已存在,则读取并校验其内容。
随后脚本会解析当前 tsconfig(包括extends继承),逐项核对compilerOptions,把缺失的"建议值"补进去、把与 webpack 配置冲突的"必填值"强制修正,最后将结果写回文件,并在终端打印变更说明。
建议值(suggested):可自由修改
以下选项在用户未显式配置时会被自动写入,你可以随意修改它们:
| 选项 | 默认建议值 | 说明 |
|---|---|---|
target | es5 | 编译目标,保证产物在旧浏览器上的兼容性 |
lib | ["dom", "dom.iterable", "esnext"] | 引入的库类型声明 |
allowJs | true | 允许混用.js文件,是渐进迁移的关键 |
skipLibCheck | true | 跳过声明文件(.d.ts)的类型检查,加快编译 |
esModuleInterop | true | 允许import React from 'react'这种默认导入写法 |
allowSyntheticDefaultImports | true | 配合esModuleInterop处理无默认导出的模块 |
strict | true | 开启严格模式(含strictNullChecks等) |
forceConsistentCasingInFileNames | true | 强制文件路径大小写一致,避免跨平台问题 |
noFallthroughCasesInSwitch | true | 禁止 switch 分支意外穿透 |
必填值(required):不可更改
以下选项由 CRA 强制固定,若用户配置了不同值,脚本会直接覆盖并打印原因,因为它们必须与 webpack 的解析行为保持一致:
| 选项 | 强制值 | 原因(源码注释) |
|---|---|---|
module | esnext | 支持import()动态导入与import/export语法 |
moduleResolution | node | 与 webpack 的模块解析方式对齐 |
resolveJsonModule | true | 匹配 webpack 的 JSON loader |
isolatedModules | true | Babel 单文件转译的实现限制 |
noEmit | true | 类型检查不产出文件,编译交给 Babel |
jsx | react-jsx(React 17+ 且 TS ≥ 4.1)或react | 支持 React 17 的新 JSX transform;旧环境回退到经典模式 |
paths | 不设置 | 别名导入不受支持(webpack.config.js 中同样注释了 aliased imports 不可用) |
include 与类型引用文件
除compilerOptions外,脚本还会确保include至少包含"src"(verifyTypeScriptSetup.js)。此外,它会检查src/react-app-env.d.ts是否存在,若不存在则写入一行/// <reference types="react-scripts" />。该声明文件是连接 CRA 内置类型(如import.meta.env、静态资源模块声明、process.env.REACT_APP_*)与你的项目的桥梁,删除它会导致部分类型丢失。
构建链路中的类型检查:ForkTsCheckerWebpackPlugin 与 TSC_COMPILE_ON_ERROR
webpack 配置中,CRA 通过fs.existsSync(paths.appTsConfig)判断项目是否启用了 TypeScript(webpack.config.js),从而决定:
- 是否在 resolve 规则中加入
.ts/.tsx扩展名解析; - 是否挂载 TypeScript 类型检查插件。
类型检查由ForkTsCheckerWebpackPlugin承担(webpack.config.js)。该插件在独立进程中运行完整的 TypeScript 类型检查,避免阻塞 webpack 主线程的编译,这也是为什么类型错误与构建错误会"同时"出现在同一控制台、但互不阻塞的原因。开发模式下插件以异步(async)方式运行,不会阻碍模块热更新。
值得特别说明的是 TSC_COMPILE_ON_ERROR 环境变量(在 advanced-configuration.md 中有完整的环境变量清单)。当设置TSC_COMPILE_ON_ERROR=true时:
- webpack 会改用 ForkTsCheckerWarningWebpackPlugin,将类型错误降级为警告(见 webpack.config.js 的插件选择逻辑);
- 即使存在类型错误,你也能照常运行和构建 TypeScript 项目;
- 错误会以警告形式打印在终端和浏览器控制台中,方便你边开发边修复。
也就是说,默认情况下类型错误是"阻断式"的(必须修复才能继续),而TSC_COMPILE_ON_ERROR=true提供了一条"容忍式"的通道,适合在大型存量项目迁移初期使用。
常见问题排查(Troubleshooting)
创建出的项目没有启用 TypeScript
如果执行--template typescript后项目仍是纯 JavaScript,很可能是npx使用了缓存的旧版create-react-app。解决办法同样是卸载全局版本:
npm uninstall -g create-react-app # 或 yarn global remove create-react-app然后在全新终端中重试创建命令,确保npx拉取的是最新版本。
从 create-react-app-typescript 迁移
如果你正在使用社区旧的create-react-app-typescript脚手架,需要先了解它与官方方案在 tsconfig 生成、类型检查接入方式上的差异,再按官方模板的结构(src下的.tsx入口、react-app-env.d.ts引用、模板依赖清单)逐步对齐。官方在 CRA 3 时代起已将 TypeScript 支持内建,无需任何第三方包。
常量枚举(const enum)与命名空间(namespace)不受支持
这是使用 Babel 编译 TypeScript 的固有约束。CRA 的转译链路是"Babel 剥离类型 + 插件独立做类型检查",而 Babel 的@babel/plugin-transform-typescript对语法有若干限制,其中就包括:
const enum不被支持(普通enum可以);namespace(除声明合并等有限场景外)不被支持;- 部分装饰器语法、
import =赋值等也不可用。
如果代码中出现了这些语法,编译会直接报错。规避方案是改用普通enum、模块化导出常量对象,或使用as const断言等现代替代写法。这一限制同样作用于所有使用 Babel 转译 TypeScript 的构建体系,并非 CRA 独有。
进阶阅读
- 环境变量与构建行为的高级配置:advanced-configuration.md(含
TSC_COMPILE_ON_ERROR、DISABLE_NEW_JSX_TRANSFORM等与 TypeScript/JSX 相关的开关) - 模板机制详解:custom-templates.md
- TypeScript 模板源码:cra-template-typescript(含 template.json 与 App.tsx)
- tsconfig 自动生成与校验实现:verifyTypeScriptSetup.js
- 类型检查插件接入点:webpack.config.js
- 官方 TypeScript 基础文档:TypeScript Handbook
【免费下载链接】create-react-appSet up a modern web app by running one command.项目地址: https://gitcode.com/gh_mirrors/cr/create-react-app
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考