Skip to content

第 2 章 工作台

📖 工具间的钥匙

"想造信号库?"主编老墨把一把黄铜钥匙放在桌上,"先得有工作台。去工具间看看吧。"

工具间在消息城最老的楼里,门一推开,灰尘在阳光里打转。屋子不大,但三样东西闪闪发光,每样都挂着一块牌子:

  • 第一样,一本厚厚的手册,封面写着"我的工具箱清单"。打开一看,里面密密麻麻记着:这个工具有什么用、怎么调用它。
  • 第二样,一块工作台面板,上面有好多旋钮和插槽,写着"规则设置"。拧紧哪个旋钮,工作台就更严格。
  • 第三样,一个自动检查员,穿着绿红两色的外套。你把东西递给它,它当场检查:"合格!"(亮绿灯)或者"不合格!这里有错!"(亮红灯)。

"这三样宝贝,一样都不能少。"老墨说,"手册叫 package.json,面板叫 tsconfig.json,检查员叫 vitest。"

小铃挠挠头:"那……它们到底是干什么的?"

"干完今天这章,你自己就明白了。开始吧。"

🎯 本章要解决什么

一句话问题:怎么搭一个能写 TypeScript、还能自动检查对错的工作台

读完这一章,你会知道:

  • 三样工具各是干什么的:package.jsontsconfig.jsonvitest
  • 怎么写你的第一个 TypeScript 函数;
  • 怎么写第一个测试,并看着它亮绿灯

💡 概念讲解

工具一:package.json(工具箱清单)

上一章你建了 signal-book 文件夹。现在我们要告诉电脑:"这是一个正经的项目,我要往里面装工具。"

怎么告诉它?写一个叫 package.json 的文件——它就是工具箱清单:里面写着项目叫什么名字、装了哪些工具、怎么运行它们。

你不需要背下它的内容。需要的时候,用一条咒语就能生成它:

bash
npm init -y

npm工具搬运工:你喊它,它就跑来跑去帮你装工具、管工具。init 意思是"开始一个新项目",-y 意思是"别问那么多问题,直接用默认设置吧"。

工具二:tsconfig.json(规则面板)

上一章我们说过,TypeScript 是 JavaScript 的大哥哥,它会给数据贴类型标签。可是,TS 要怎么知道该严格到什么程度?答案就写在 tsconfig.json 里——它像工作台的规则面板

书里每一章,我们用的都是同一块面板(内容见"章末完整代码")。它的核心规则是:

json
{
	"strict": true
}

strict 就是"严格"的意思。把它拧到 true,工作台就会非常较真:类型不对,立刻大喊。严是好事——错误在写代码时就发现,总比程序跑起来才崩好。

工具三:vitest(自动检查员)

最后是今天的主角:vitest。它是一个测试工具——自动检查员。

怎么用?你写一个"检查单"(测试文件),在上面写明:"我觉得这个函数应该返回 6"。然后 vitest 把函数真的跑一遍,对比结果:

  • 一样 → 亮绿灯(通过)
  • 不一样 → 亮灯(失败,还会告诉你差在哪)
mermaid
graph LR
    src["src/money.ts 📄<br/>你的代码"] --> vitest["vitest 🧪 自动检查员"]
    tests["tests/money.spec.ts 📋<br/>你的检查单"] --> vitest
    vitest --> ok["绿 ✓ 通过"]
    vitest --> bad["红 ✗ 失败,并告诉你差在哪"]

为什么测试这么重要?因为接下来 19 章,我们每改一次代码,都可能把前面弄好的东西改坏。有了检查员,每次改完跑一遍 npm test,它替我们盯着——"放心,你改的地方没弄坏别的东西。" 这叫回归测试,以后你会天天听到这个词。

✍️ 动手写代码

开工!这次我们要敲不少命令,别怕,都是套路。

第 1 步:走进项目文件夹

打开终端,走进上一章建的文件夹:

bash
cd signal-book

第 2 步:生成工具箱清单

bash
npm init -y

🚀 现在跑一下,你应该看到:终端里打印出一个小表格,里面有 name: "signal-book"version 之类的东西。然后文件夹里多了一个 package.json 文件。

第 3 步:请搬运工装工具

bash
npm install -D typescript vitest

-D 的意思是"这两个工具只在开发时用"(以后送给别人玩的时候,不用带上它们)。

🚀 现在跑一下,你应该看到:一堆像下雨一样的文字滚过去,最后出现类似 added 100+ packages 的字样。这时文件夹里多了一个 node_modules 文件夹——这是工具们的仓库,你永远不用管它里面有什么。

第 4 步:在清单里登记"怎么运行测试"

打开 package.json,你会看到 npm install 已经把工具写进清单了(devDependencies 那一段)。现在我们要再加一条:告诉工作台,"运行测试"这个动作叫什么。找到 "scripts" 这一行(现在应该是空的 "scripts": {}),把它改成:

json
"scripts": {
	"test": "vitest run"
}

改完的 package.json 大概长这样(版本号可能和你的略有不同,没关系):

json
{
	"name": "signal-book",
	"version": "1.0.0",
	"scripts": {
		"test": "vitest run"
	},
	"devDependencies": {
		"typescript": "^5.7.3",
		"vitest": "^3.0.5"
	}
}

以后我们只要喊 npm test,工作台就知道:去把检查员叫来,把所有测试跑一遍。

第 5 步:立规则面板

新建一个文件 tsconfig.json,把下面的内容抄进去(这是书里唯一的版本,后面每章都一样):

json
{
	"compilerOptions": {
		"target": "ES2022",
		"module": "ESNext",
		"moduleResolution": "bundler",
		"strict": true,
		"noEmit": true,
		"skipLibCheck": true
	},
	"include": ["src", "tests"]
}

不用理解每一行,知道 strict: true 是"严格模式"就够了。include 是说"要检查 srctests 两个文件夹里的代码"。

第 6 步:写第一个 TypeScript 函数

新建 src 文件夹,在里面建 money.ts,抄进下面的代码:

ts
// 小铃的赚钱计算:每份报纸赚 2 块钱。
export function money(count: number): number {
	return count * 2;
}

这是你的第一个 TypeScript 函数!注意两个地方和 JS 不一样:

  • count: number —— 参数 count 贴上了"数字"标签;
  • ): number —— 返回的东西也要是数字。

第 7 步:写检查单(第一个测试)

新建 tests 文件夹,在里面建 money.spec.ts

ts
import { describe, expect, test } from 'vitest';
import { money } from '../src/money';

describe('小铃的赚钱计算', () => {
	test('送 1 份报纸,赚 2 元', () => {
		expect(money(1)).toBe(2);
	});

	test('送 3 份报纸,赚 6 元', () => {
		expect(money(3)).toBe(6);
	});
});

读一读这张检查单,它很像人话:

  • describe('小铃的赚钱计算', ...) —— "接下来要检查的主题是:小铃的赚钱计算";
  • test('送 1 份报纸,赚 2 元', ...) —— "有一个测试叫:送 1 份报纸赚 2 元";
  • expect(money(1)).toBe(2) —— "我期望:money(1) 的结果等于 2"。

expect 是"期望",toBe 是"等于"。检查员的记事本就是这两句话。

第 8 步:叫检查员来!

回到终端,喊:

bash
npm test

🚀 现在跑一下,你应该看到(这就是传说中亮绿灯的时刻):

✓ tests/money.spec.ts (2 tests)
Test Files  1 passed (1)
     Tests  2 passed (2)

两个测试全过了!你的工作台搭好了。🎉

🧰 TS 小课堂:第一次正式开张!

今天学三样 TS 语法,都是以后天天用的。

1. 类型标注

给变量或参数贴"类型标签":

ts
let count: number = 1;     // count 是数字
let name: string = '小铃';  // name 是文字
let ok: boolean = true;    // ok 是"是/否"

三种最常见的标签:number(数字)、string(文字)、boolean(是/否)。

如果标签贴错了,比如 let count: number = '三';,工作台会立刻大喊:"这里类型不对!"——这就是严格模式的好处。

2. 函数的"签名"

export function money(count: number): number 的尾巴是函数的签名:它声明了"进门要带什么(参数类型)、出门会交出什么(返回类型)"。以后看书里的库函数,先看签名,就能猜到它怎么用。

3. export 和 import(公告栏)

  • export —— 把自己的东西摆上公告栏,别人才能拿;
  • import —— 从公告栏取东西

测试文件第一行 import { money } from '../src/money' 就是在说:"去 src/money 的公告栏上,把 money 拿过来。"(.. 表示"上一级文件夹")

📚 工具箱

下面是本章的三样工具。注意:它们叫"工具",不是消息城术语,所以不进书末尾的正式词典——记住它们的作用就行:

工具它是什么一句话记住它
npm工具搬运工帮你装工具、管工具
package.json工具箱清单写着装了啥、怎么运行
tsconfig.json规则面板里面 strict: true 就是严格模式
node_modules工具们的仓库永远不用打开它
vitest自动检查员跑测试,绿=对,红=错
npm test召唤检查员的咒语每次改完代码就喊一次

📦 章末完整代码

这一章你的 signal-book 文件夹应该长这样:

signal-book/
├── demo/
│   └── ch01.mjs            ← 第 1 章的,还在
├── node_modules/           ← 工具仓库,不用管
├── package.json
├── tsconfig.json
├── src/
│   └── money.ts
└── tests/
    └── money.spec.ts

package.json 完整内容:

json
{
	"name": "signal-book",
	"version": "1.0.0",
	"scripts": {
		"test": "vitest run"
	},
	"devDependencies": {
		"typescript": "^5.7.3",
		"vitest": "^3.0.5"
	}
}

tsconfig.json 完整内容:

json
{
	"compilerOptions": {
		"target": "ES2022",
		"module": "ESNext",
		"moduleResolution": "bundler",
		"strict": true,
		"noEmit": true,
		"skipLibCheck": true
	},
	"include": ["src", "tests"]
}

src/money.ts 完整内容:

ts
// 小铃的赚钱计算:每份报纸赚 2 块钱。
export function money(count: number): number {
	return count * 2;
}

tests/money.spec.ts 完整内容:

ts
import { describe, expect, test } from 'vitest';
import { money } from '../src/money';

describe('小铃的赚钱计算', () => {
	test('送 1 份报纸,赚 2 元', () => {
		expect(money(1)).toBe(2);
	});

	test('送 3 份报纸,赚 6 元', () => {
		expect(money(3)).toBe(6);
	});
});

跑通了? 只要 npm test 能打出 2 passed,工作台就搭好了。从下一章开始,我们真的要开始造信号库了!

🎮 动手试试

怎么玩

先合上答案,自己在本子上写出答案,再点开对照。忍得住才点开哦!

1. 预言输出(猜一猜)

小铃在 money.spec.ts 里加了一个测试:

ts
test('送 10 份报纸', () => {
	expect(money(10)).toBe(20);
});

猜猜看:跑 npm test 会亮绿还是

再猜一个:let count: number = '三'; 这一行,工作台会大喊吗?

👉 点开看答案

第一题:绿money(10) 等于 10 × 2 = 20,检查员说"对!"。

第二题:会大喊'三' 是文字(string),却贴了数字(number)的标签,严格模式立刻报错。类型标签不是装饰品,是工作台的规矩。

2. 帮小铃找 bug(抓错)

小铃想给 money 写个测试,可它总是红灯

ts
test('送 2 份报纸', () => {
	expect(money(2)).toBe(6);
});

money 函数错了,还是小铃的期望写错了?

👉 点开看答案

期望写错了money(2)2 × 2 = 4,不是 6。小铃的检查单写错了,函数没错。改成 toBe(4) 就绿了。

红灯不可怕,它是在说"你的检查单和代码,有一个错了"——检查员比我们诚实

3. 填空(补全)

小铃要给蛋糕店写个函数 cake:每个蛋糕 5 块钱。补全下面函数的签名

ts
export function cake(count /* 这里少了点什么? */) /* 这里也少了点什么? */ {
	return count * 5;
}
👉 点开看答案

两处都补 : number

ts
export function cake(count: number): number {
	return count * 5;
}

(如果写完想验证,再补一个测试:expect(cake(2)).toBe(10);

4. 小挑战(改代码)

主编宣布:"从明天起,每份报纸赚 3 块钱!"

小铃改完 src/money.ts 里的 return count * 2 变成 return count * 3,跑 npm test——红了

这是为什么?小铃应该怎么办?

👉 点开看答案

红是因为检查单还是旧的money.spec.ts 里写着"送 3 份赚 6 元",可函数现在返回 9 元,检查员当然喊红。

这是测试存在的意义:当代码行为改变时,它会提醒你"检查单也要跟着改"。把 money.spec.ts 里的期望改成新价格(送 1 份赚 3 元、送 3 份赚 9 元),再跑就绿了。

以后每章我们改代码,都要这样:改代码 → 跑测试 → 绿了才放心

🏁 本章小结

  1. package.json 是工具箱清单,tsconfig.json 是规则面板,vitest 是自动检查员;
  2. 测试就是你写的检查单:expect(结果).toBe(期望),绿=对,红=错;
  3. 从今往后,每次改完代码都要跑 npm test——检查员会替我们盯住所有旧功能。

下一章,正式开工造信号库的第一个零件:信号