☰
VSCode 搭建 TypeScript 环境:安装、编译与断点调试指南
2026/10/1 4:43:24 网站建设 项目流程

带过几届新人之后我发现一个挺反直觉的现象:真正把初学者劝退的,往往不是语法本身,而是第一次打开编辑器之后的那半小时。VSCode 装好了,TypeScript 也听说过,但 Node 是什么关系、tsc 为什么敲了没反应、报红的一堆波浪线到底该不该管,这些问题凑在一起,很容易让人误以为自己不适合写代码。我写这篇东西的目的很直接,就是把 VSCode 搭建 TypeScript 环境 这件事从头到尾拆开讲一遍,包括每一步背后为什么要这么做,以及我在实际带人过程中收集到的坑。

这篇内容适合三类人:完全没碰过 Node 生态的前端新手;写了几年 JavaScript 想切到 TypeScript 但一直被配置劝退的老手;以及需要给团队新人写一份能直接照抄的入门文档的人。整套流程在 Windows、macOS、Linux 上都能跑通,差异点我会单独标出来。全程不依赖任何收费工具,装完之后你拿到的是一个能补全、能报错、能断点调试、能一键编译的最小可用环境,剩下的框架和工程化都可以往上叠。

1. 先把地基打好:为什么是 VSCode 加 TypeScript 这套组合

动手之前先花五分钟把"为什么"想清楚,比闷头装十个插件有用得多。我见过太多人环境装得花里胡哨,结果连 tsc 和 ts-node 的区别都说不清,一遇到报错就抓瞎。这一节不写操作,只讲判断依据,想直接动手的可以跳到第二节。

1.1 TypeScript 到底补上了 JavaScript 的哪个缺口

JavaScript 是动态类型语言,变量的类型在运行时才确定。写小脚本的时候这很爽,随手let a = 1就完事;但项目一旦超过几百行,问题就来了。你把某个函数的参数从userName改名成username,编辑器不会提示你还有三个调用点没改,只有等用户点进那个页面、控制台抛出Cannot read property of undefined,你才知道出事了。这种错误在 JavaScript 里是常态,而 TypeScript 的价值就是把这类错误从"运行时"提前到"敲代码时"。

打个比方,JavaScript 像一箱没有标签的零件盒,你凭记忆知道哪个螺丝配哪个孔;TypeScript 则是给每个零件贴了标签,还配了一张装配图。标签不会让零件更好用,但它能让你在拿错的时候立刻察觉。TypeScript 在编译阶段做静态类型检查,编辑器里的波浪线、红色提示、悬停时的类型信息,全都来自它。

代价当然也有。你需要多写类型标注,需要多一个编译步骤,需要理解接口、泛型、类型收窄这些概念。所以我的建议很明确:几十行的脚本、一次性的数据处理任务,直接用 JavaScript 更省事;只要这个项目你打算维护超过一个月,或者会有第二个人来看你的代码,TypeScript 的投入产出比就立刻转正了。

1.2 为什么我最终把 VSCode 定为默认编辑器

编辑器选择是个老话题,我的结论是:对绝大多数人来说 VSCode 是最没争议的答案,原因不在功能多少,而在"零配置起步"这四个字。TypeScript 是微软主导的语言,VSCode 也是微软的产品,所以语言服务是内置的。你写完npm install typescript之后,什么都不用配,VSCode 就会自动识别项目里的 TypeScript 版本,补全、跳转定义、重命名符号、查找所有引用这些功能全部开箱可用。

对比一下其他选择:WebStorm 的智能提示确实更细腻,重构能力也强,但它是收费软件,启动慢,吃内存也凶,对刚入门的人来说有点重;Sublime Text 启动飞快,可它的 TypeScript 支持基本靠社区插件拼装,配置成本反而更高;Vim 和 Neovim 那一套,除非你本来就习惯了模态编辑,否则前期学习成本会盖过一切收益。

还有一个容易被忽视的点:VSCode 的远程开发体验做得很完整。如果你在公司用的是 Windows 但代码跑在 Linux 服务器上,可以直接用 Remote 相关功能连过去,在远端目录里编辑,语言服务也跑在远端,本地机器完全不卡。这套工作流我在好几家公司都用过,稳定性没问题。新手暂时用不上,但知道有这么条路,将来遇到场景不会慌。

1.3 开工前的三项准备和两个禁忌

第一项准备是装 Node.js。很多人会问:我就想学 TypeScript,为什么非要装 Node?道理很简单,TypeScript 的编译器tsc本身就是一个用 JavaScript 写的程序,它需要 Node 运行时才能跑起来;而npm(Node 自带的包管理器)是你安装 TypeScript、安装类型声明文件的唯一入口。没有 Node,后面所有命令都执行不了。

第二项准备是规划项目目录。这里有两个我必须强调的禁忌:一是路径里不要出现中文和空格,Windows 上某些工具链对非 ASCII 路径处理得不好,会出现莫名其妙的乱码或找不到文件的报错;二是不要把项目放在 OneDrive、iCloud、坚果云这类同步盘目录里,文件监听和同步进程会打架,表现为编辑器反复重新加载、tsc --watch频繁触发编译,甚至出现文件被锁定的情况。我自己的习惯是在用户目录下建一个纯英文的code文件夹,所有练习项目都往里放。

第三项准备是心态。TypeScript 报错密集,尤其第一次打开strict模式的时候,一个刚写完的文件可能瞬间飘出十几条红线。这不是你写错了,这是它在帮你排查。学会读报错、理解报错,是这段学习里最有价值的部分,比背语法有用得多。

2. 从零安装到项目跑起来:完整配置流程

这一节是实操部分,我会把每个命令的作用和每个配置项的取舍都讲清楚。你可以照着敲,但建议边敲边看解释,否则下次换个项目你还是不知道怎么配。

2.1 装 Node.js 并验证版本

去 Node.js 官网下载 LTS(长期支持)版本。为什么是 LTS 而不是 Current?因为 LTS 版本的依赖生态适配最完整,各种工具链都测过,出问题的概率最低。下载完成后一路默认安装即可,Windows 用户注意安装向导里有个 "Add to PATH" 的勾选项要保证是勾上的,否则命令行里敲 node 会提示找不到命令。

装完打开终端(Windows 用 PowerShell 或 CMD,macOS 用 Terminal),执行:

node -v npm -v

正常的话会分别输出类似v20.11.1和10.2.4的版本号。这里有个细节:Node 的版本号规则是主版本.次版本.补丁。主版本偶数的是 LTS 线,奇数是过渡版本,生命周期短。如果你是团队协作,最好和同事对齐主版本号,不然容易在依赖安装上出现差异,这就是业内常说的"在我机器上是好的"的经典来源。

如果你需要在多个 Node 版本之间切换(比如同时维护两个要求不同版本的老项目),可以再装一个版本管理工具,Windows 上是 nvm-windows,macOS 和 Linux 上是 nvm。安装前记得先卸载干净已装的 Node,否则会冲突。新手可以先跳过这一步,等项目多了再说。

2.2 VSCode 的下载安装与界面汉化

VSCode 官网下载对应系统的安装包。Windows 安装过程中有几个选项值得勾上:把"通过 Code 打开"添加到文件资源管理器右键菜单、把 VSCode 注册为常见文件类型的编辑器。这两个能让后续操作顺手很多。macOS 用户把下载的 app 拖进应用程序文件夹就行,然后建议在命令面板里执行一次安装code命令行工具的操作,这样在终端里可以直接用code .打开当前目录。

界面汉化有两种做法,效果一样,看你习惯哪种。第一种是启动 VSCode 后按Ctrl+Shift+P(macOS 是Cmd+Shift+P)打开命令面板,输入Configure Display Language,选择中文,它会提示你安装中文语言包,确认后重启即可。第二种是直接在扩展市场搜索 "Chinese (Simplified) Language Pack",装完重启。我个人更推荐第二种,因为你能顺便熟悉扩展面板的位置。

这里插一句:汉化只是个人偏好,不影响任何功能。我建议新手先用中文界面把概念建立起来,等熟悉了再切回英文。原因很简单,绝大多数报错信息和官方文档都是英文的,长期看英文界面能帮你形成条件反射,看到Cannot find module就知道是模块找不到,不需要在脑子里翻译一遍。

2.3 插件清单:装什么,以及为什么可以不装

扩展市场里 TypeScript 相关插件多到吓人,但真正必要的不多。下面这张表是我给新人列的清单,分了三档,按需取用。

插件名作用建议
Chinese (Simplified) Language Pack界面汉化新手推荐
ESLint代码质量检查必装
Prettier代码格式化必装
Error Lens把报错直接显示在行尾强烈推荐
Path Intellisense路径自动补全推荐
GitLensGit 历史与行内注解推荐
Code Spell Checker英文拼写检查可选
各类主题、图标包视觉美化随意

需要说明的是,VSCode 内置的 TypeScript 语言服务已经提供了补全、跳转、重构、查找引用这些核心能力,不需要再额外装任何"TypeScript 支持"的插件。市面上一些号称增强提示的插件,本质是在内置能力上加壳,装多了反而会出现两个语言服务打架、提示重复的情况。我踩过这个坑:同时装了三个提示类插件,结果保存一次代码要等两秒才更新波浪线,卸掉之后立刻恢复流畅。

Error Lens 这个插件我特别想说一下,它把类型错误、ESLint 警告直接渲染在代码行末尾,不用把鼠标悬停上去就能看见。对新手理解类型系统帮助极大,你写const n: number = "abc",它会当场告诉你不能把字符串赋给数字类型,学习反馈是实时的。

2.4 项目初始化与 tsconfig.json 逐项拆解

找一个人英文路径的文件夹,在里面打开终端,依次执行:

mkdir ts-demo cd ts-demo npm init -y npm install --save-dev typescript npx tsc --init

四条命令的意思分别是:建目录、进入目录、生成默认的 package.json、把 TypeScript 作为开发依赖装到本地、生成一份带注释的 tsconfig.json 模板。这里我特意用了--save-dev而不是全局安装,原因很重要:全局安装的版本对所有项目都是一个,而不同项目对 TypeScript 版本的要求可能不一样;本地安装则每个项目自带一份,版本隔离,换电脑或者换同事也能保证一致。用npx tsc而不是直接tsc,就是为了强制使用项目内的那一份。

生成的 tsconfig.json 会有一大堆被注释掉的选项,我建议全部删掉,换成下面这份精简版,然后逐条理解:

{ "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "rootDir": "./src", "outDir": "./dist", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "sourceMap": true, "declaration": false, "forceConsistentCasingInFileNames": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] }

target决定编译后代码的语法版本。设成 ES2022 意味着现代语法原样保留,运行时要求 Node 16 以上。设太低会做大量降级转换,代码变丑;设太高则可能在老环境跑不起来。做 Node 服务端开发,跟着你的 Node 版本走就行,Node 20 对应 ES2022 是安全的。

module和moduleResolution涉及模块系统,是新手最容易糊的地方。简单说,Node 生态有两套模块规范:老的 CommonJS(require)和新的 ES Module(import)。NodeNext的含义是"跟着 package.json 里的 type 字段走",如果你的 package.json 里有"type": "module",就按 ES Module 处理,否则按 CommonJS。我的建议是新建学习项目就用NodeNext,配上"type": "module",因为这是当前的方向。

rootDir和outDir分别指定源码目录和输出目录。指定它们的好处是编译后的目录结构可预测,不会出现dist/src/index.js这种多套一层的情况。如果你不指定,TypeScript 会自己推断,推断结果有时候和你想的不一样,尤其是当你把配置文件放进了被 include 的目录里。

strict是重头戏,它是一个总开关,一次性打开包括strictNullChecks、noImplicitAny、strictFunctionTypes在内的一整组严格检查。我的态度很坚决:新项目一律开,不要关。关掉之后null和undefined会悄悄混进你的类型里,等于把 TypeScript 最值钱的那部分能力扔了。老项目迁移可以先用strict: false过渡,之后逐个文件开启。

skipLibCheck: true是跳过对node_modules里.d.ts文件的类型检查。这能让编译速度明显变快,代价是第三方库的类型错误会被忽略。实际项目里几乎没人关掉它,因为第三方类型声明出错你也没法改。

sourceMap: true会生成.js.map文件,把编译后的代码映射回源码。这是断点调试能正常工作的前提,必须开。没有它,你在编辑器里打断点,运行时停在编译后的乱码上,根本没法看。

3. 写出第一个能跑的项目:编译、运行与调试三条链路

配置到位之后,真正的乐趣才开始。这一节我会带你写完一个带类型的小模块,然后分别用三种方式跑起来,最后配好断点调试。走完这一遍,你对 TypeScript 的运行机制就有了直觉。

3.1 三种运行方式怎么选

同样一段 TypeScript 代码,有三种跑法,各有适用场景:

方式原理优点缺点适用场景
tsc + node先编译成 JS,再用 Node 执行最贴近生产环境,产物可见多一步,每次改代码要重编译打包发布、排查编译期问题
ts-node运行时即时编译一条命令直接跑 TS启动慢,类型检查可跳过临时脚本、学习练手
tsx基于 esbuild 转译启动快,体验接近原生不做类型检查,只擦除类型本地开发热重载

我的实际用法是这样的:学习阶段用 tsx 跑得快,改完立刻见效;需要验证类型错误的时候手动跑一次tsc --noEmit;准备部署的时候用 tsc 编译出 dist 目录。ts-node 现在我用得少了,主要是启动速度确实比 tsx 慢一截,尤其是项目大了之后。

安装方式:

npm install --save-dev tsx

如果你还想试试 ts-node:

npm install --save-dev ts-node @types/node

注意@types/node这个包,它提供了 Node 内置模块(比如fs、path)的类型声明。不装的话,你import fs from "fs"会直接报找不到模块声明。它是纯类型包,编译后不产生任何运行时代码,所以装成开发依赖就行。

3.2 手写一个带类型的模块并编译

在项目根目录建src文件夹,写两个文件。先写src/types.ts:

export interface Task { id: number; title: string; done: boolean; tags?: string[]; } export type TaskFilter = "all" | "active" | "done";

再写src/index.ts:

import { Task, TaskFilter } from "./types.js"; const tasks: Task[] = [ { id: 1, title: "装好 Node 和 VSCode", done: true, tags: ["环境"] }, { id: 2, title: "写出第一个接口", done: false }, ]; function filterTasks(list: Task[], filter: TaskFilter): Task[] { if (filter === "all") return list; if (filter === "done") return list.filter((t) => t.done); return list.filter((t) => !t.done); } function format(t: Task): string { const mark = t.done ? "[x]" : "[ ]"; const tags = t.tags?.length ? ` #${t.tags.join(" #")}` : ""; return `${mark} #${t.id} ${t.title}${tags}`; } const result = filterTasks(tasks, "all"); result.forEach((t) => console.log(format(t)));

这里有三个细节值得停下来看。第一,import的路径写了./types.js而不是./types,也没有写.ts。这看着别扭,但它是 Node ESM 的硬性要求:运行时必须带扩展名,而运行时看到的是编译后的.js文件,所以源码里就得写.js。TypeScript 会在编译时把它解析到对应的.ts文件上。这个规则是新手第一天最容易撞墙的地方,后面常见问题部分我会再展开。

第二,TaskFilter用的是联合类型而不是枚举。联合类型零运行时开销,编辑器还能精确推断分支,if (filter === "done")这句之后它会自动把类型收窄成"done"。枚举编译后会生成额外对象,非必要我一般不用。

第三,t.tags?.length里的问号是可选链,配合strict模式下的空值检查,能安全处理tags可能不存在的情况。如果没有这个问号,TypeScript 会直接提示你tags可能是 undefined,不允许你访问.length。

现在编译:

npx tsc

没有任何输出就是成功。你会看到生成了一个dist目录,里面是index.js、types.js和对应的.map文件。对比一下源码和产物:类型标注全部消失了,接口和类型别名也不见了,联合类型被擦除,只剩下纯粹的 JavaScript。这就是所谓"类型擦除",TypeScript 不改变运行时行为,它只是加了一层编译期的保护壳。

执行产物:

node dist/index.js

如果你已经配置了"type": "module",输出会类似:

[x] #1 装好 Node 和 VSCode #环境 [ ] #2 写出第一个接口

如果想跳过编译直接用 tsx 跑源码:

npx tsx src/index.ts

结果一样,但省掉了编译步骤。开发时用这条,发布前跑一次完整的 tsc。

3.3 配好断点调试,比 console.log 强十倍

新手最先学会的调试手段是console.log,这没错,但它的局限很明显:你只能在事先想到的位置打印,遇到循环、递归、异步回调的时候,打印出来的日志能淹死人。断点调试可以让你在任意位置暂停程序、查看当前所有变量的值、单步执行、观察调用栈,效率完全不是一个量级。

在项目根目录建.vscode文件夹,里面放一个launch.json:

{ "version": "0.2.0", "configurations": [ { "name": "调试当前 TS 文件", "type": "node", "request": "launch", "program": "${workspaceFolder}/node_modules/tsx/dist/cli.mjs", "args": ["${file}"], "console": "integratedTerminal", "skipFiles": ["<node_internals>/**"] } ] }

打开src/index.ts,在function format那一行的行号左侧点一下,会出现一个红点,这就是断点。按 F5(macOS 上是 Fn+F5 或直接点左侧调试图标),程序会启动并停在断点处。此时左侧面板会显示当前作用域下所有变量:t是什么、mark是什么、tags是什么,一目了然。顶部工具栏有继续、单步跳过、单步进入、单步跳出四个按钮,分别对应 F5、F10、F11、Shift+F11。

几个实操心得。skipFiles这个配置用来告诉调试器跳过 Node 内部文件,不加的话你按单步进入很容易掉进 Node 源码里出不来,得点几十次才能回到自己的代码。console设为integratedTerminal的好处是输出会走集成终端,支持颜色和交互;如果用默认的调试控制台,某些库输出的彩色日志会变成一堆转义字符。

还有一点:调试用的是 tsx 来跑源码,所以 source map 是由 tsx 提供的,不需要依赖 tsc 生成的 map。这两种方式不冲突,但别搞混。如果你把program指向dist/index.js,那就走 tsc 生成的 source map,前提是sourceMap: true开着,且dist已是最新。我个人更推荐调试源码这条路线,改完立刻能调,不用等编译。

3.4 用 npm scripts 把命令固化下来

每次手敲npx tsc太累,也容易敲错。在 package.json 里加一组脚本:

{ "type": "module", "scripts": { "dev": "tsx watch src/index.ts", "build": "tsc", "start": "node dist/index.js", "typecheck": "tsc --noEmit" } }

tsx watch会监听文件变化并自动重启,改完代码不用手动执行,这是日常开发最常用的命令。build负责产出。start跑产物,模拟线上环境。typecheck只做类型检查不输出文件,适合放在提交前的检查流程里。

有一点要提醒:tsc --noEmit和tsc的检查强度是一样的,区别只是不写文件。我见过有人在开发时用 tsx 跑得很欢,结果 CI 上一跑 tsc 报出二十个错误,原因就是 tsx 只擦除类型不做检查。所以每天至少完整跑一次 typecheck,把这个习惯养起来,能省掉很多返工。

4. 踩坑实录:新手最容易卡住的问题清单

这一节是全文我最想让你认真看的部分。下面这些坑,基本每个我带过的人都踩过至少三个。我把它们整理成速查表,遇到报错先来这里对一遍,大概率能直接定位。

4.1 常见报错速查表

报错信息根本原因解决方式
Cannot find module 'xxx' or its corresponding type declarations缺少类型声明文件装@types/xxx,或自己写.d.ts声明
Cannot use import statement outside a module模块规范不匹配package.json 加"type": "module",或改 tsconfig 的 module
'xxx' is declared but its value is never read开了noUnusedLocals且有未使用变量删掉变量,或给无用参数加下划线前缀
Object is possibly 'undefined'strictNullChecks生效加可选链?.或显式判空
File 'xxx' is not under 'rootDir'被编译的文件超出了 rootDir 范围挪进 src,或调整 rootDir
Type 'string' is not assignable to type 'number'类型不匹配按提示修正,别用 as any 硬压
tsc 不是内部或外部命令全局未装且在错误的目录执行用npx tsc,或确认当前在项目根目录
中文路径下编译乱码或找不到文件工具链对非 ASCII 路径支持不佳项目移到纯英文路径

关于最后一条我想多说两句。as any是新手最容易上瘾的"解药"。报错了?加个as any,红线立刻消失。但这么做的本质是把类型系统关掉了,你付出学习成本却一点收益都没拿到。我的建议是给自己定一条规矩:只有在你确认这是第三方库的类型定义有问题时才允许用 any,并且必须写注释说明原因。其他情况下宁可多花十分钟去理解报错在说什么。

还有@types/xxx这个规律要记住:绝大多数主流 JavaScript 库的类型声明都发布在@types作用域下,由社区维护。判断一个库有没有自带类型,最直接的办法是看它有没有index.d.ts文件,或者去类型声明的检索站点搜一下包名。搜不到的话,就得自己写一个.d.ts文件声明模块,这是进阶内容,遇到再说。

4.2 路径、模块与 ESM 的几个深坑

import必须带扩展名这件事,前面提过一次,这里系统说一下。在 Node 的 ES Module 规范里,相对导入必须写完整的文件路径,包括扩展名。TypeScript 源码里你写的是./types.js,但因为编译后确实是types.js,所以运行时能对上。你写./types.ts会报错,写./types在某些配置下也会报错。

这个规则和 CommonJS 时代完全不同。CommonJS 里require("./types")是标准写法,Node 会自己尝试补全.js、.json、.node。所以很多老教程教你省略扩展名,照抄到 ESM 项目里立刻报错。判断自己的项目走哪套规范,看 package.json 有没有"type": "module"就够了。

另一个坑是__dirname和__filename。这两个变量在 CommonJS 里是内置的,但在 ES Module 里不存在。如果你要用它们来定位文件路径,得这样写:

import { fileURLToPath } from "node:url"; import { dirname } from "node:path"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename);

这段代码我建议直接存成片段,用到就复制。无数人在这上面卡过半小时,报错信息还是__dirname is not defined,看着像是 Node 出问题了,其实是模块规范的原因。

关于baseUrl这个选项,如果你看到别人教程里用它来配路径别名,这里要提醒一句:baseUrl已经被标记为弃用,在未来的 TypeScript 版本中会被移除。现在的推荐做法是在paths里直接用相对 tsconfig 所在目录的路径,不再依赖baseUrl:

{ "compilerOptions": { "paths": { "@/*": ["./src/*"] } } }

不过要说清楚,这个改动只影响 TypeScript 的类型解析。运行时 Node 并不认识@/这种别名,你还需要额外的方案来处理运行时路径映射,比如打包工具或者运行时加载器。新手阶段建议先不要碰路径别名,老老实实写相对路径,等环境完全跑通、对模块解析机制有感觉了再折腾。过早引入别名,会让报错信息变得难以理解,因为编译期和运行时的解析结果不一致时的报错非常隐晦。

4.3 插件冲突与编辑器卡顿的处理

VSCode 里有一个很少人知道但非常关键的设置:内置 TypeScript 版本可以切换。打开任意.ts文件,按Ctrl+Shift+P,输入TypeScript: Select TypeScript Version,你会看到两个选项:一个是 VSCode 自带的版本,一个是工作区里node_modules安装的版本。一定要选工作区那个。

为什么?因为编辑器提示和 tsc 编译使用的必须是同一个版本。如果编辑器用内置的 5.0,项目装的是 5.4,新语法在编辑器里标红但编译能过,或者反过来,那种"编辑器报错但能跑"的诡异现象就是这么来的。团队里可以把这个选择写进.vscode/settings.json:

{ "typescript.tsdk": "node_modules/typescript/lib" }

卡顿问题通常在项目变大之后出现。语言服务需要扫描所有被 include 的文件,如果 node_modules 没排除干净,扫描量会爆炸。检查一下 tsconfig 里的exclude,同时在 VSCode 设置里加一条文件监听排除:

{ "files.watcherExclude": { "**/node_modules/**": true, "**/dist/**": true } }

我在一个中型项目上实测过,加上这两条排除之后,冷启动的语言服务初始化时间从十几秒降到了三秒以内,保存时的自动提示延迟也明显降低。这不是玄学,就是扫描范围小了。

5. 把环境用顺手:格式化、规范与个人配置分享

环境能跑只是起点,真正让它好用的是后面这些顺手的小配置。这一节的内容都不复杂,但每一条我都在实际项目里用了很久,属于"配一次省一年"的类型。

5.1 接入 ESLint 与 Prettier 的分工

这两个工具容易搞混,先分清职责:Prettier 管格式,ESLint 管质量。缩进几个空格、要不要分号、行宽多少,这些是格式问题,交给 Prettier;变量声明了没用、用了==而不是===、函数复杂度太高,这些是质量问题,交给 ESLint。让它们各管一摊,就不会互相打架。

安装:

npm install --save-dev eslint prettier eslint-config-prettier

eslint-config-prettier这个包的作用是关掉 ESLint 里所有和格式相关的规则,避免两个工具给出矛盾的修复建议。这一步非常关键,不装的话你会遇到"保存一次代码格式被改两遍、互相来回推"的诡异现象,我当年被这个折腾了一整个下午才找到原因。

然后在项目根目录建.prettierrc:

{ "semi": true, "singleQuote": false, "printWidth": 100, "trailingComma": "es5", "tabWidth": 2 }

printWidth设 100 是我个人的偏好,默认的 80 在现代宽屏上换行太频繁,读起来反而累。这个值团队内统一就行,没有绝对的对错,重要的是统一。

最后在.vscode/settings.json里打开保存自动格式化:

{ "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" } }

注意source.fixAll.eslint的值,新版本 VSCode 要求写"explicit"而不是true,写错了不生效也不报错,很多人卡在这里以为是插件坏了。这个细节文档里藏得比较深,我是查 issue 才找到的。

5.2 我个人在用的完整配置片段

把前面的内容合起来,我现在的.vscode/settings.json大致长这样,可以直接抄:

{ "typescript.tsdk": "node_modules/typescript/lib", "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, "files.watcherExclude": { "**/node_modules/**": true, "**/dist/**": true, "**/.git/**": true }, "files.exclude": { "**/dist": false }, "typescript.preferences.importModuleSpecifierEnding": "js", "typescript.updateImportsOnFileMove.enabled": "always" }

其中typescript.preferences.importModuleSpecifierEnding设为js,是为了让编辑器自动补全导入路径时带上.js后缀,正好匹配 ESM 的要求,省得你每次手动改。updateImportsOnFileMove设为always,意思是移动或重命名文件时自动更新所有引用它的导入路径,这个功能在重构时能救命,强烈建议开。

files.exclude里我把dist设成了false,也就是不隐藏。原因是学习阶段我建议你多看看编译产物,观察类型是怎么被擦除的、interface编译后变成了什么、enum生成了多少额外代码。这种观察对建立直觉特别有帮助。等项目大了,产物多了看着烦,再改成 true 隐藏掉。

5.3 从这套最小环境往前走

环境搭完之后,往哪个方向走取决于你的目标。想写后端服务,可以看看基于 TypeScript 的服务端框架,装饰器、依赖注入那一套用起来很舒服,但要注意它对 tsconfig 有些特定要求,比如experimentalDecorators和emitDecoratorMetadata得打开。想做前端,主流框架的脚手架基本都内置了 TypeScript 模板,一条命令就生成好完整配置,你只需要看懂它生成的 tsconfig 就行,而经过今天这一遍,你已经有能力看懂了。

再给一个判断标准。如果你现在打开一个陌生项目的 tsconfig,能说出strict、target、moduleResolution这三项各是干什么的,那你已经脱离小白阶段了。这三项覆盖了 TypeScript 配置里百分之八十的实际问题。

我个人在这些年反复配置环境的过程中,最大的体会是:不要追求一次配到完美。我见过有人在项目第一天就花两天时间研究 monorepo 配置、路径别名、构建缓存,结果代码一行没写,热情先耗没了。正确的顺序是先用最小配置跑通一个能运行的脚本,然后随着遇到的真实问题逐个往上加。每加一个配置,你都知道它是为了解决什么具体问题才存在的,这样配出来的东西你才真正掌握,而不是从别人仓库里复制一堆看不懂的 JSON。环境这件事,够用就好,能跑、能调、能报错,就已经覆盖了入门阶段的全部需求。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询