环境搭建与初体验
安装工具、配置编译器、写出第一个 .ts 文件
🎯 学习目标
学完本章,你将能够:
- 能够独立完成 TypeScript 开发环境搭建(Node.js、tsc、tsconfig)
- 能够理解 tsconfig.json 核心配置项的作用与推荐设置
- 能够使用 tsc 编译 TS 文件并用 tsx 直接运行
- 能够配置 ESLint 与 VS Code 实现开发体验优化
- 能够解决常见的环境配置问题与模块解析错误
怎么开始
浏览器不认识 .ts 文件,那 TypeScript 代码怎么运行?
答案很简单:先把 .ts 编译成 .js,然后像平时一样运行。
你需要三样东西:一个编译器(tsc)、一个配置文件(tsconfig.json)、一个能直接运行 TS 的工具(tsx)。
Part 1: 安装编译器
TypeScript 编译器是一个 npm 包,装好就能用
浏览器不认识 .ts 文件,那 TypeScript 代码怎么运行?答案是先编译成 JS。你需要一个编译器——tsc,还有开发时直接运行 TS 的工具 tsx。这一节只需要几行命令,把编译环境搭起来。安装完成后,你的终端就能识别 .ts 文件了。
全局安装
最简单的方式,一行命令搞定
$ npm install -g typescript
$ tsc --version
Version 5.4.5
安装完成后,tsc 命令全局可用。但更推荐项目本地安装,方便团队统一版本。
项目本地安装
推荐方式,团队协作版本一致
$ npm install -D typescript
$ npx tsc --version
# 同时安装 tsx 用于直接运行 TS 文件
$ npm install -D tsx
# 直接运行 TS 文件
$ npx tsx src/index.ts
| 包名 | 用途 | 安装命令 |
|---|---|---|
typescript | TS 编译器 | npm i -D typescript |
tsx | 直接运行 TS 文件 | npm i -D tsx |
ts-node | TS 执行引擎 | npm i -D ts-node |
@types/node | Node.js 类型定义 | npm i -D @types/node |
tsx 比 ts-node 快得多(基于 esbuild),推荐新项目使用 tsx。
Part 2: 配置文件
tsconfig.json 告诉编译器怎么编译你的代码
编译器装好了,但它怎么知道你的源码在哪、编译输出放哪、要不要开严格模式?这些全写在 tsconfig.json 里。一行命令就能生成它,但里面那些配置项到底是什么意思?这一节从"开箱即用"到"理解每一行配置",帮你彻底搞懂 tsconfig。
tsconfig 速览
一条命令生成,几个关键选项就够了
$ npx tsc --init
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
strict 严格模式
一行配置开启所有严格检查
"strict": true 等于同时开启以下所有检查:
| 选项 | 作用 |
|---|---|
strictNullChecks | null/undefined 不能赋值给其他类型 |
noImplicitAny | 禁止隐式 any |
strictFunctionTypes | 函数参数类型严格协变 |
strictPropertyInitialization | 类属性必须初始化 |
noImplicitThis | 禁止隐式 this |
alwaysStrict | 始终严格模式解析 |
strict 模式。虽然初期会多写一些类型,但能避免大量运行时错误。
tsconfig 进阶配置
理解几个关键选项,让配置不再"复制粘贴"。
模块解析策略
moduleResolution 决定 TS 怎么找 import 的文件
| 模式 | 行为 | 适用场景 |
|---|---|---|
"node" | 按 Node.js 的规则解析,支持目录下的 index | 旧项目、CommonJS 模块 |
"node16" | Node.js 16+ 的精确解析,要求写明扩展名 | Node.js ESM 项目 |
"bundler" | 兼容 Vite/webpack 等打包工具的解析规则 | 前端项目、用 tsx 运行的项目 |
// bundler 模式下可以省略扩展名
import { User } from "./types/user"; // bundler ✅ node16 ❌
// node16 要求写明 .js 扩展名(即使源文件是 .ts)
import { User } from "./types/user.js"; // node16 ✅
tsx 运行的 Node.js 项目选 "bundler",最省心。如果用纯 Node.js 运行 ESM,选 "node16"。
其他重要选项
每个选项解决一个具体问题
{
"compilerOptions": {
"verbatimModuleSyntax": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"noEmit": false
}
}
| 选项 | 作用 | 什么时候开 |
|---|---|---|
verbatimModuleSyntax | 强制用 import type 导入纯类型,确保 tree-shaking 生效 | 推荐始终开启 |
declaration | 生成 .d.ts 类型声明文件 | 写库给别人用时 |
declarationMap | 为 .d.ts 生成 sourceMap,方便从使用方跳回源码 | 库作者调试时 |
sourceMap | 生成 .js.map 文件,浏览器/调试器能对应回 .ts 源码 | 需要断点调试时 |
verbatimModuleSyntax 的好处:它强制你区分"值导入"和"类型导入",打包工具就能安全地移除只用作类型的 import,减小最终产物体积。
实战 tsconfig
一个 Node.js 后端项目的推荐配置
{
"compilerOptions": {
// 语言与模块
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
// 输出
"outDir": "./dist",
"rootDir": "./src",
"sourceMap": true,
// 严格检查
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
// 模块互操作
"esModuleInterop": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"resolveJsonModule": true,
// 加速
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
noUnusedLocals 和 noUnusedParameters 会在有未使用变量时报错。如果你正在快速原型开发,可以暂时关掉这两个。
@types 类型生态
第三方库没有 TS 类型怎么办?
JavaScript 生态有大量第三方库,不是所有库都自带 TypeScript 类型。这时候需要 DefinitelyTyped 社区提供的类型包。
# 搜索某个库有没有类型包
$ npm info @types/lodash
# 有输出说明存在这个类型包
# 常见的 @types 包
$ npm install -D @types/node # Node.js API 类型
$ npm install -D @types/express # Express 框架类型
$ npm install -D @types/jest # Jest 测试框架类型
$ npm install -D @types/react # React 类型
| 情况 | 怎么办 | 例子 |
|---|---|---|
| 库自带 TS 类型 | 直接用,不用装 @types | zod、prisma |
| 有 @types/xxx | 安装对应的 @types 包 | @types/express |
| 啥都没有 | 自己写 .d.ts 声明文件 | 内部自研库 |
// types/shims.d.ts —— 给没有类型的库写声明
declare module "some-untyped-lib" {
export function doSomething(input: string): number;
}
DefinitelyTyped(github.com/DefinitelyTyped)是最大的社区类型仓库,几乎主流 JS 库都能在上面找到类型定义。当你 npm install 一个库后,VS Code 提示"任何",就去查查有没有 @types 包。
ESLint + TypeScript
tsc 只管类型检查,代码风格和潜在问题还得靠 ESLint。
配置 ESLint
安装与基本配置,五分钟搞定
$ npm install -D eslint @eslint/js typescript-eslint
// eslint.config.mjs(ESLint 9+ 扁平配置)
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
{
rules: {
// 允许 any 但要求显式声明(不忽略)
"@typescript-eslint/no-explicit-any": "warn",
// 未使用的变量报错(排除下划线前缀)
"@typescript-eslint/no-unused-vars": [
"error",
{ argsIgnorePattern: "^_" }
],
// 强制用 import type 导入类型
"@typescript-eslint/consistent-type-imports": "error"
}
}
);
tsc 只做类型检查(这是"对不对"),而 ESLint 还能检查未使用的变量、复杂度过高的函数、不一致的导入风格等(这是"好不好")。两者互补,不冲突。
运行 ESLint
命令行跑一遍,集成到 scripts 里自动化
# 检查所有 .ts 文件
$ npx eslint src/
# 自动修复能修的问题
$ npx eslint src/ --fix
# 只检查不改
// package.json
{
"scripts": {
"dev": "tsx watch src/index.ts",
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"typecheck": "tsc --noEmit",
"build": "tsc"
}
}
| 命令 | 作用 | 何时运行 |
|---|---|---|
npm run typecheck | tsc 类型检查 | 每次提交前 / CI |
npm run lint | ESLint 代码质量检查 | 每次提交前 / CI |
npm run lint:fix | 自动修复 lint 问题 | 开发中随手修 |
推荐流程:typecheck 管类型对不对,lint 管代码好不好。在 CI 中两个都跑:npm run typecheck && npm run lint。
Part 3: 第一个 .ts 文件
创建一个 TypeScript 文件,体验类型检查的效果
编译器和配置都就绪了,该写点代码了。这一节创建你的第一个 .ts 文件,用接口定义对象形状,给函数参数加类型注解。然后故意写一个类型错误,看看编译器怎么"秒抓"。编译前后对比一下,你会发现类型注解被完全擦除,输出的就是干净的 JS。
编写 hello.ts
接口 + 类型注解 + 故意犯错
// src/hello.ts
interface User {
name: string;
age: number;
}
function greet(user: User): string {
return `Hello, ${user.name}! You are ${user.age} years old.`;
}
const user: User = { name: "张三", age: 25 };
console.log(greet(user));
// 故意制造一个类型错误
// const wrong: User = { name: 42 }; // 报错!name 应该是 string
注意 interface 定义了对象的形状,: string 和 : number 是类型注解。写错了编译器会标红。
编译与运行
三种方式,从慢到快
# 方式1:先编译再运行
$ npx tsc
$ node dist/hello.js
# 方式2:用 tsx 直接运行(推荐开发时用)
$ npx tsx src/hello.ts
# Hello, 张三! You are 25 years old.
# 方式3:编译单个文件
$ npx tsc src/hello.ts --outDir dist
开发时用 tsx 即时运行,发布时用 tsc 编译输出。
编译前后对比
类型注解被完全擦除
源码 (.ts)
const user: User = {
name: "张三",
age: 25
};
编译后 (.js)
const user = {
name: "张三",
age: 25
};
所有类型注解(: User)在编译后被完全擦除。浏览器和 Node.js 看到的还是纯 JavaScript。
tsx 快捷运行
开发时的最佳搭档
# 直接运行 TS 文件
$ npx tsx src/index.ts
# 监听模式——文件变化自动重新运行
$ npx tsx watch src/index.ts
// package.json 中的脚本配置
{
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"start": "node dist/index.js"
}
}
# tsx 还提供交互式 TS REPL
$ npx tsx
> let x: number = 42
> x
42
> x = "hello" // 报错:不能将 string 赋值给 number
Part 4: VS Code 的 TS 加持
VS Code 对 TypeScript 有一流的内置支持,无需额外装扩展
代码能跑起来了,但每次都要手动编译也太麻烦了。好消息是 VS Code 内置了完整的 TypeScript 支持。输入 . 自动提示属性,按 F2 安全重命名,Ctrl+. 一键修复类型错误。这一节掌握几个关键快捷键和调试配置,让你的 TS 开发效率直接翻倍。
核心快捷键
这些快捷键会让你的 TS 开发效率翻倍
| 功能 | 快捷键 | 说明 |
|---|---|---|
| 跳转到定义 | F12 | 跳到变量/函数/类型的定义位置 |
| 查看引用 | Shift+F12 | 查看哪些地方使用了它 |
| 重命名符号 | F2 | 安全重命名,所有引用自动更新 |
| 悬停查看类型 | 鼠标悬停 | 显示完整类型信息 |
| 自动补全 | Ctrl+Space | 智能提示属性、方法 |
| 快速修复 | Ctrl+. | 自动修复类型错误 |
| 错误面板 | Ctrl+Shift+M | 查看所有类型错误 |
实用技巧
几个让开发更顺畅的小窍门
// 1. 内联查看类型:Ctrl+Shift+点击类型名
// 不跳转文件就能看接口的完整定义
// 2. 自动导入:输入变量名后 Ctrl+. 自动添加 import
// 3. 重构提取:选中代码后 Ctrl+Shift+R
// - 提取函数 / 提取接口 / 提取类型别名
// 4. 选择 TS 版本
// Ctrl+Shift+P → "TypeScript: Select TypeScript Version"
Ctrl+Shift+P,输入 TypeScript: Restart TS Server 重启 TypeScript 服务。
快速修复技巧
用 Ctrl+. 解决大部分类型报错
光标放在报错的位置,按 Ctrl+.,VS Code 会弹出可用的修复方案:
| 问题 | 快速修复方案 |
|---|---|
| 缺少某个属性 | 自动添加缺失的属性到对象 |
| 类型不匹配 | 建议添加类型断言或修改类型 |
| 找不到模块 | 自动添加 import 语句 |
| 未使用的导入 | 删除未使用的 import |
| 函数缺少返回类型 | 推断并添加返回类型注解 |
# 批量操作也很方便:
# Ctrl+Shift+P → "Add all missing imports"
# 一次性给整个文件补全所有缺失的 import
# Ctrl+Shift+P → "Organize imports"
# 排序 import 语句 + 删除未使用的导入
养成习惯:看到红色波浪线,先按 Ctrl+. 看看编辑器能不能帮你修。大部分时候一个回车就搞定了。
调试与配置
断点调试 TS + 切换 TS 版本 + 内联提示
断点调试 .ts 文件:需要 sourceMap: true,然后直接在 .ts 文件里打断点。
// .vscode/launch.json —— 用 tsx 调试当前文件
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug TS",
"type": "node",
"request": "launch",
"runtimeExecutable": "npx",
"runtimeArgs": ["tsx", "${file}"],
"console": "integratedTerminal"
}
]
}
内联提示(Inlay Hints):在编辑器里直接显示参数名和类型推断结果。
// .vscode/settings.json
{
"typescript.inlayHints.parameterNames.enabled": "all",
"typescript.inlayHints.functionLikeReturnTypes.enabled": true,
"typescript.inlayHints.variableTypes.enabled": true
}
切换 TS 版本:Ctrl+Shift+P → TypeScript: Select TypeScript Version → 选择工作区版本。这个在升级 TS 版本后特别有用。
sourceMap 是否为 true,以及 rootDir 和 outDir 配置是否正确。
项目目录
一个规范的 TS 项目长什么样?
my-ts-project/
├── src/ # 源码目录(.ts 文件放这里)
│ ├── index.ts # 入口文件
│ ├── utils/ # 工具函数
│ │ └── format.ts
│ └── types/ # 类型定义
│ └── user.ts
├── dist/ # 编译输出(自动生成,不要手动改)
│ └── index.js
├── types/ # 全局类型声明(.d.ts)
│ └── global.d.ts
├── tsconfig.json # TS 编译配置
├── package.json
└── node_modules/
用人话说:src/ 是你的工作区,dist/ 是编译器的输出区。types/ 存放共享的类型定义,相当于项目的"合同"。记住——永远不要手动编辑 dist/ 里的文件。
编译全流程
从 .ts 到运行结果,中间经历了什么?

图:TypeScript 编译流程
【AI 生图提示词】请生成一张中文技术流程图,风格简洁、配色柔和、适合教学: - 画面主体:两条并行路径 - 上方路径(标准流程):src/hello.ts → tsc 类型检查 → 类型擦除 → dist/hello.js → node 运行 → 输出结果 - 下方路径(快捷路径):.ts → tsx 直接运行 → 输出结果(用虚线箭头标注"开发快捷方式") - 两条路径最终汇聚到同一个"输出结果"节点 - 配色建议:浅蓝/浅灰背景,标准流程用实线蓝色箭头,快捷路径用虚线绿色箭头 - 画面比例:16:9 - 避免:过度装饰、3D 效果、杂乱的颜色
# 完整流程:先编译,再运行
$ npx tsc # src/hello.ts → dist/hello.js
$ node dist/hello.js # 运行编译后的 JS
# 快捷流程:tsx 一条命令搞定
$ npx tsx src/hello.ts # 编译 + 运行,一步到位
| 方式 | 命令 | 输出文件 | 适用场景 |
|---|---|---|---|
tsc + node | 两步执行 | 有 .js 输出 | 生产部署 |
tsx | 一步执行 | 无中间文件 | 本地开发 |
开发工具对比
tsx watch vs tsc --watch vs nodemon,该选哪个?
| 工具 | 作用 | 文件监听 | 速度 | 推荐场景 |
|---|---|---|---|---|
tsx watch | 直接运行 TS + 监听 | 文件变化自动重启 | 极快 | TS 项目开发首选 |
tsc --watch | 监听并编译 TS | 文件变化自动编译 | 中等 | 需要查看编译输出时 |
nodemon | 监听 JS 变化重启 | 需配合 tsc 使用 | 较慢 | 旧 JS 项目迁移 |
// package.json 中推荐的 scripts 配置
{
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"typecheck": "tsc --noEmit",
"start": "node dist/index.js"
}
}
typecheck 脚本只做类型检查不输出文件,适合在 CI/CD 中使用。dev 用 tsx watch 开发体验最丝滑。
综合练习
创建一个多文件 TS 项目,体验 import/export
// src/types/user.ts —— 定义类型
export interface User {
id: number;
name: string;
email: string;
role: "admin" | "member";
}
export interface ApiResponse {
code: number;
data: T;
message: string;
}
// src/utils/format.ts —— 工具函数
import type { User } from "../types/user.js";
export function formatUser(user: User): string {
return `[${user.role}] ${user.name} <${user.email}>`;
}
export function createGuest(): User {
return { id: 0, name: "访客", email: "", role: "member" };
}
// src/index.ts —— 入口文件
import type { User, ApiResponse } from "./types/user.js";
import { formatUser, createGuest } from "./utils/format.js";
const guest = createGuest();
const response: ApiResponse = {
code: 200,
data: guest,
message: "ok"
};
console.log(formatUser(response.data));
期望输出
$ npx tsx src/index.ts
[member] 访客 <>
常见环境问题
问题一:tsc --version 提示"命令未找到"
原因:TypeScript 没有全局安装,或不在 PATH 中。
修复:用项目本地安装 npm i -D typescript,然后 npx tsc。
问题二:VS Code 里 .ts 文件没有类型提示
原因:项目根目录缺少 tsconfig.json。
修复:运行 npx tsc --init 生成配置文件。
问题三:npx tsx 运行报模块找不到
原因:没有安装 tsx,或路径写错了。
修复:npm install -D tsx,确认文件路径正确。
试一试
练习一
任务:创建一个 TS 项目,编译并运行。
$ mkdir ts-demo && cd ts-demo
$ npm init -y
$ npm install -D typescript tsx
$ npx tsc --init
// 创建 src/index.ts
const message: string = "Hello, TypeScript!";
console.log(message);
$ npx tsx src/index.ts
期望输出
Hello, TypeScript!
练习二
任务:在 hello.ts 中故意写一个类型错误,观察编译器的报错信息。
// 试试这行代码,看看编译器怎么提示
const wrong: number = "hello";
参考答案
error TS2322: 不能将类型"string"分配给类型"number"。
📝 要点总结
本章核心知识点
安装方式
全局安装 tsc,项目本地安装 typescript 依赖
tsconfig
strict、module、target、moduleResolution 核心选项
编译运行
tsc 编译 JS,tsx/ts-node 直接运行 TS
类型生态
@types/* 包提供第三方库类型定义
开发工具
ESLint + Prettier + VS Code 插件提升效率
下一章
环境准备好了,下一章学习基础类型注解——给变量、数组、对象标注类型。