TypeScript 是什么、为什么需要它 — 从动态类型的坑说起¶
这一章讲三件事: TypeScript 到底是什么(它其实不是一种「能跑的语言」); 为什么 JavaScript 的灵活会在大项目里变成灾难;以及它补齐的面向对象能力有什么用。 读完你会拿到全书的出发点:后面十五章,全是在这一章换来的类型安全之上盖楼。
1. 先看一个真实的坑:两个数相加,结果不是数¶
JavaScript 是全世界用得最多的语言之一。书里引了 GitHub 的统计: 到 2025 年,JavaScript 仍是 GitHub 上近 80% 新仓库在用的六种核心语言之一; 而就在 2025 年 8 月,TypeScript 的使用者数量超过了 Python 和 JavaScript 本身, GitHub 称这是十多年来最大的一次语言格局变动3。
这么多人用,问题不在「人少」,在语言本身的脾气。看原书的第一个例子:
你想让两个数相加,期望打印 11。但运行结果是 56。
为什么?因为 b 其实是个字符串,JavaScript 的 + 号看见两边有一边是字符串,
就把另一边也转成字符串、拼接起来4。5 被悄悄变成了 '5',拼在 '6' 前面。
这种「悄悄替你转换类型」的行为叫类型强制(type coercion)——它平时像便利,
出错时像背刺:程序不报错,只是结果不对,而且这个错一直潜伏到你真正用它的时候。
再看第二个坑。原书有一个判断用户能不能开车的函数,它读用户的 age 字段:
function canDrive(usr) {
if (usr.age >= 16) { /* 允许开车 */ }
}
const tom = { name: "tom" } // 注意:tom 没有 age 字段
canDrive(tom);
tom 对象压根没有 age。JavaScript 不会拦你,运行到 usr.age 时它拿到一个特殊的空值
undefined,这个空值在比较里被当成「假」,于是一个 30 岁但字段写错名字的用户,
会被判成不许开车——程序照样不报错5。
这两类错有一个共同点:都要等程序真正跑起来、跑到那一行,才暴露。 小脚本无所谓,大项目里这样的行有几十万,你不可能每行都跑到。
2. 顶层全景:TypeScript 是语言加编译器,而且代码永远不在机器上跑¶
原书给 TypeScript 下的定义很干脆——它是两项技术6:
你写的 .ts 文件
│
▼
TypeScript 编译器(tsc)── 两件事同时发生 ──┐
│ │
├─ ① 检查:类型对不对?不对就报错,不出成品 │
▼ │
② 翻译:产出纯 JavaScript(.js 文件) ◄──────┘
│
▼
浏览器或 Node 运行的是这份 .js —— 机器上从来没有 TypeScript
图说:TypeScript 是「开发期」技术。它管的是写完代码到代码运行之间的那段时间。
TypeScript 是开发时技术——这是全书最该记住的一句话。机器上没有任何 TypeScript 在跑;
编译器把 .ts 翻译成 .js,跑的是 JavaScript7。这个翻译过程有个专门的名字,
叫转译(transpilation):和编译不同,它不是变成机器码,而是从一种语言变成另一种语言8。
所以它的价值也全在开发期:错误在你敲代码的时候就被标出来,而不是等用户替你发现。 书里说,这等于给开发流程加了纪律——错误在开发阶段就被迫修掉9。
插曲:第一次转译的完整一圈(原书手把手部分)¶
在讲类型之前,书里先带读者把工具链走了一遍,这一圈本身就是现代 Node 项目的 标准开机动作,值得原样记住:
npm init → 生成 package.json(项目的「户口本」:名字、版本、
scripts 脚本区、dependencies 依赖区)
npm install -g typescript(书里为示例选了全局安装;实际项目应装到项目里)
→ 获得 tsc 命令(Mac 上多半要加 sudo)
建 string-vs-number.ts → 注意 .ts 扩展名:这是 TypeScript 的专属扩展名,
编译器靠它认出要处理的文件
tsc string-vs-number.ts → 同时做两件事:检查类型 + 产出同名的 .js
node string-vs-number.js → 跑的是翻译产物,不是 .ts
原书还交代了一个后续章节反复用的细节:package.json 里有两个区——
scripts 存命令快捷方式(npm run xxx 的 xxx),dependencies 存项目依赖;
TypeScript 要求 5.5 或更高1。本书后面的每一章,开机动作都是这三板斧的变体。
3. 核心原理一:静态类型 = 把「这个变量里应该装什么」写在明处¶
原书对「类型」的定义朴素得惊人:类型就是一套可复用的规则,用来描述一个对象或变量10。
JavaScript 是动态类型:变量不需要声明类型,装过数字的变量随时可以改装字符串。
TypeScript 走另一条路——静态类型:创建变量时就把类型标明,之后不许变11。
标法的专用名词叫类型注解(type annotation),就是在变量后面写 : number 这样的字。
回到第 1 节的坑,各看一眼修法(这就是本章的主线例子:同一个用户年龄判断, 从裸 JavaScript 一路改到类型安全的版本):
主线例子:判断用户能不能开车(canDrive)
第 ① 步 裸 JavaScript
canDrive(tom) 照常跑,tom 没有 age 也不报错 → 运行时才爆
第 ② 步 给变量加类型注解
let b: number = '6'
→ 编译器当场报错:Type 'string' is not assignable to type 'number'
→ 数字字符串拼接那一类错,在写代码阶段就被消灭
第 ③ 步 用 interface 给「用户」这个形状定合同
interface User { name: string; age: number }
function canDrive(usr: User) { ... }
→ 调用 canDrive(tom) 时,tom 缺 age 字段,编译器再次报错
→ 原话:Property 'age' is missing in type '{ name: string; }' but required in type 'User'
→ 对象缺字段那一类错,同样在运行之前被消灭
第 ③ 步里出现了一个新东西 interface User。原书把它叫作合同(contract):
一个不带实现、只定形状的类型——任何号称是 User 的对象,必须有 name 和 age 两个字段12。
函数参数一标 : User,编译器就会自动检查每一个调用点传进来的对象合不合合同。
这就是静态类型真正的威力来源:不是你写了一次检查,而是每个调用点都被自动检查了一遍。
书里还补了一个时间坐标:从 TypeScript 6.0 起,tsconfig.json 里的 strict
(最严格的一档检查)默认开启,新项目开箱就是最高强度检查13。
4. 核心原理二:面向对象的四根支柱,JavaScript 缺了两根半¶
第二个要补的能力是面向对象编程(OOP)。原书列的现代四支柱是: 封装、抽象、继承、多态14。逐个看 JavaScript 的缺口在哪、TypeScript 怎么补。
先给坐标:2025 年的语言格局¶
原书开篇用了一组数字立论,值得原样记住量级:GitHub 上近 80% 的新仓库在用的 六种核心语言里有 JavaScript;而 2025 年 8 月,TypeScript 的使用者数量超过 Python 和 JavaScript 本体登顶,年增长超 100 万贡献者、增速 66.63%——GitHub 称之为 十多年来最大的一次语言格局变动2。这本书的存在本身就是这个变动的注脚: 不是 JavaScript 不行了,而是大项目多到「灵活」本身成了最大的成本。
封装:把数据藏进容器,只留受控的口¶
封装换个说法就是信息隐藏:把数据连同操作它的方法装进一个叫类(class)的容器,
容器外的东西不能直接碰数据,只能走类提供的入口15。
TypeScript 控制入口的工具叫访问修饰符:private 只允许类内部访问,
public 允许内外访问;再配合 getter/setter(读取器/写入器),
类就握住了「谁能读、谁能改、怎么改」的决定权16。
主线例子的支线:Encapsulator 类
class Encapsulator {
private name: string ← 字段藏起来了
get getName(): string { return this.name } ← 只读口
set setName(name: string) { this.name = name } ← 只写口
}
new Encapsulator("John").name → 编译器拒绝:private
new Encapsulator("John").getName → 拿到 "John"
书里有个细节值得记住:原生 JavaScript 现在也能用 # 前缀藏字段了,
这本书通篇优先用 JavaScript 标准语法——TypeScript 刻意贴着 JavaScript 的官方标准
(ECMAScript)走,凡是 JavaScript 已经能表达的,就不再用 TypeScript 的私有写法17。
四支柱与 Java 系语言的对照(原书的隐含读者背景)¶
书里反复强调「几乎所大型应用语言(Java、C++、C#)都提供静态类型和 OOP」, 这句话其实是给有 Java/C# 背景的读者一颗定心丸,也给了所有人一张对照表: 封装(private/public + getter/setter)、抽象(interface/abstract class)、 继承(extends/super)、多态(对接口编程)在 TypeScript 里一样不少, 且语法刻意贴近这些语言;真正的差异在两条:TypeScript 的 private 只在编译期生效 (Java 的运行时反射语义不同),以及结构类型让「是否写了 implements」成为可选而非必须 (03 章的结构类型会把这个差异讲透)。
抽象:只露签名,不露实现¶
抽象和封装是亲戚:把「数据怎么管」藏起来,对外只给一个简化的接口。
目的叫松耦合——管一摊数据的代码和别的代码互不依赖,
改一边不伤另一边18。机制是 interface(接口)或抽象类:像壳,
只暴露成员的名字和类型,不写实现19。JavaScript 不支持接口和抽象类,
TypeScript 两者都支持20。
代码上就是第 3 节那个 interface User,再加一个实现它的类:
interface User { name: string; age: number; canDrive(): void }
class Person implements User { /* 把 canDrive 真正实现出来 */ }
const john: User = new Person("john", 15);
implements(实现)这个词是合同的另一面:接口声明「必须有这些成员」,
类签字画押负责把它们做出来21。于是调用方只对着 User 这个壳写代码,
底下换成任何实现都无所谓——这就是松耦合的实际形状。
继承:写一次,处处复用¶
继承解决的是代码复用。书里的例子:商店卖自行车、卡车、摩托车,
每样都写一遍 id、description、price 太浪费;抽出一个基类 Item 装公共部分,
Bicycle 用 extends(继承)关键字直接拿来,再添自己特有的 wheelCount22。
JavaScript 的继承靠原型继承(prototypal inheritance): 同一类的所有实例共享同一个核心对象(原型),成员挂原型上,实例各自引用。 省内存,但表达力有限23。TypeScript 的类不但能继承类,还能继承接口和抽象类, 复用的路子多得多24。
多态:对「一般类型」写代码,运行时再定「具体是谁」¶
多态和继承配套:对着基类型写代码,具体执行哪个实现,运行时才知道; 调用方不需要、也不该关心拿到的到底是哪个子类25。 JavaScript 靠动态类型天然支持这一点,但它缺编译期把关: 你传一个缺了方法的对象进来,没有任何东西拦你,只能等运行到那行才炸。 TypeScript 补的正是这一环——接口和类型约束让不匹配在代码运行前就被抓住26。
5. 作者的判断与证据¶
这一章几乎全是「共识级」的说法,作者的推理链条是:JavaScript 为简单场景而生 → 大项目需要静态类型和完整 OOP → 多年里出现过好几种补法,TypeScript 是被采用最广、 活得最久的那个27。证据主要是两类:
- 使用量的证据(GitHub Octoverse 2025 统计,引在第 1 节)——TypeScript 超车的规模和增速3;
- 演示性的证据——本章所有类型错误都是编译器真实报出的原文(如
Type 'string' is not assignable to type 'number')4,这类证据是「可复现的」, 读者自己敲一遍就能看到。
有一处是作者的判断而非事实:「静态类型让代码更清晰、更少 bug、更好重构」是行业广泛接受的 主张,但书里给的是例证,不是对照实验数据。我们认可这个判断,但见第 6 节的边界。
6. 边界与局限¶
- 只讲了「为什么」,没讲「怎么用好」。 泛型(generics)在第 4 节末尾被预告了, 正文说「要等第 2 章学完才能讲透多态的完整例子」,本章的多态只有文字没有完整代码25。
- 严格的代价没展开。
strict默认开启意味着大量「原本能跑」的 JavaScript 写法 在 TypeScript 下要改写;迁移存量项目的痛苦,这一章只字未提。 - 时间坐标:2026 年。 书里所有版本号(TypeScript 6、Node 24)是当时的; 这类数字会过期,机制不会。后文各章遇到具体版本我们都会标时点。
判断(我们的,不是书里的): 这一章真正的教学价值不在 OOP 四支柱的讲解 (任何教材都有),而在它先让你踩到动态类型的坑,再给解药的顺序—— 类型注解和 interface 合同出现之前,你已经亲眼见过两类运行时才爆的错。 这个顺序值得在教任何一门静态类型语言时复用。 如果错,会错在: 「踩坑再给药」对有 JavaScript 实战经验的人有效; 对完全的新手,坑本身可能读不出痛感,顺序优势会打折。
7. 可带走的¶
- TypeScript = 一门语言 + 一个编译器;跑在机器上的永远是它翻译出来的 JavaScript。
- 转译(transpilation)是从一种语言到另一种语言,不是编译成机器码。
- 动态类型的两大暗坑:
+的类型强制拼接、对象缺字段拿到undefined被当「假」。 - 静态类型把这两类错从「运行时爆炸」提前到「写代码时报红」。
- 类型注解
: number是显式声明;interface是给对象形状定的合同。 - OOP 四支柱:封装(信息隐藏 + 访问修饰符)、抽象(接口/抽象类、松耦合)、 继承(extends 复用)、多态(对一般类型写代码,编译期把关)。
- TypeScript 贴着 ECMAScript 标准走;JavaScript 能表达的(如
#私有字段), 这本书优先用 JavaScript 写法。 - 从 TypeScript 6.0 起
strict默认开启——新项目默认最严格检查。
8. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| GitHub 统计与语言格局变动 | Chapter 1: Understanding TypeScript | text/04-ch01-chapter-1-understanding-typescript.txt:13(搜「overtook both Python and JavaScript」) |
| TypeScript = 语言 + 编译器 | 同上 | text/04-ch01-chapter-1-understanding-typescript.txt:93(搜「two distinct but related」) |
| 开发时技术、转译 | 同上 | text/04-ch01-chapter-1-understanding-typescript.txt:109(搜「development-time technology」) |
| 类型强制拼接的坑 | 同上 | text/04-ch01-chapter-1-understanding-typescript.txt:480(搜「type coercion」) |
| 类型注解与编译器报错 | 同上 | text/04-ch01-chapter-1-understanding-typescript.txt:516(搜「Type Annotation」) · text/04-ch01-chapter-1-understanding-typescript.txt:526(搜「not assignable」) |
| strict 默认开启 | 同上 | text/04-ch01-chapter-1-understanding-typescript.txt:653(搜「strict flag」) |
| interface 即合同 | 同上 | text/04-ch01-chapter-1-understanding-typescript.txt:691(搜「contract」) |
| OOP 四支柱 | 同上 | text/04-ch01-chapter-1-understanding-typescript.txt:882(搜「Encapsulation」) |
| 抽象与松耦合 | 同上 | text/04-ch01-chapter-1-understanding-typescript.txt:1099(搜「Loose coupling」) |
| JavaScript 不支持接口与抽象类 | 同上 | text/04-ch01-chapter-1-understanding-typescript.txt:1119(搜「does not support interfaces」) |
| 原型继承 | 同上 | text/04-ch01-chapter-1-understanding-typescript.txt:1259(搜「Prototypal」) |
| 多态缺编译期把关 | 同上 | text/04-ch01-chapter-1-understanding-typescript.txt:1397(搜「compile-time enforcement」) |
-
出处:「Chapter 1: Understanding TypeScript」第 259–340 段(
text/04-ch01-chapter-1-understanding-typescript.txt:294,搜「section called」;:338,搜「5.5 or later」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 11–19 段(
text/04-ch01-chapter-1-understanding-typescript.txt:13,搜「overtook both Python and JavaScript」)。数字为书中转述的 GitHub Octoverse 2025 报告,属第三方统计。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 13 段(
text/04-ch01-chapter-1-understanding-typescript.txt:13,搜「overtook both Python and JavaScript」)。原文含具体数字:增长 66.63%、年增超 100 万贡献者。引的是 GitHub Octoverse 2025 报告,属于书里转述的第三方统计。 ↩↩ -
出处:「Chapter 1: Understanding TypeScript」第 480 段(
text/04-ch01-chapter-1-understanding-typescript.txt:480,搜「type coercion」)。例子代码在原文第 410–412 段(text/04-ch01-chapter-1-understanding-typescript.txt:410,搜「let a = 5」)。 ↩↩ -
出处:「Chapter 1: Understanding TypeScript」第 572 段与第 572 段(
text/04-ch01-chapter-1-understanding-typescript.txt:572,搜「function canDrive」;:601,搜「resolve to false」)。「falsy」的提法见第 605 段(搜「falsy」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 93–99 段(
text/04-ch01-chapter-1-understanding-typescript.txt:93,搜「two distinct but related」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 109–113 段(
text/04-ch01-chapter-1-understanding-typescript.txt:109,搜「development-time technology」)。「没有任何 TypeScript 代码在机器上运行」是原文第 111 段的原话(搜「no TypeScript code ever runs」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 117–123 段(
text/04-ch01-chapter-1-understanding-typescript.txt:117,搜「transpilation」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 105 段(
text/04-ch01-chapter-1-understanding-typescript.txt:105,搜「discipline」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 362 段(
text/04-ch01-chapter-1-understanding-typescript.txt:362,搜「of reusable rules that describe an objec」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 364–380 段(
text/04-ch01-chapter-1-understanding-typescript.txt:364,搜「dynamically typed」;:380,搜「up front」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 689–691 段(
text/04-ch01-chapter-1-understanding-typescript.txt:689,搜「without an implementation」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 653 段(
text/04-ch01-chapter-1-understanding-typescript.txt:653,搜「strict flag」)。这是 2026 年时点(TypeScript 6.0)的情况。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 880–888 段(
text/04-ch01-chapter-1-understanding-typescript.txt:880,搜「widely accepted modern list」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 894–908 段(
text/04-ch01-chapter-1-understanding-typescript.txt:894,搜「information hiding」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 918–924 段(
text/04-ch01-chapter-1-understanding-typescript.txt:918,搜「private」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 934–944 段(
text/04-ch01-chapter-1-understanding-typescript.txt:936,搜「symbol, and we will prefer this JavaScri」;:938,搜「ECMAScript」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 1095–1103 段(
text/04-ch01-chapter-1-understanding-typescript.txt:1099,搜「Loose coupling」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 1107–1113 段(
text/04-ch01-chapter-1-understanding-typescript.txt:1107,搜「interface or abstract class」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 1119 段(
text/04-ch01-chapter-1-understanding-typescript.txt:1119,搜「does not support interfaces」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 1195 段(
text/04-ch01-chapter-1-understanding-typescript.txt:1195,搜「implements」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 1237–1247 段与 1297–1321 段(
text/04-ch01-chapter-1-understanding-typescript.txt:1237,搜「code reuse」;:1307,搜「extends Item」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 1255–1269 段(
text/04-ch01-chapter-1-understanding-typescript.txt:1259,搜「Prototypal」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 1271 段(
text/04-ch01-chapter-1-understanding-typescript.txt:1271,搜「inherit from interfaces」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 1387–1391 段与第 1387 段(
text/04-ch01-chapter-1-understanding-typescript.txt:1387,搜「general type」;:1401,搜「don't know enough」)——原文明说本节讲不透,完整例子留到第 2 章。 ↩↩ -
出处:「Chapter 1: Understanding TypeScript」第 1397–1399 段(
text/04-ch01-chapter-1-understanding-typescript.txt:1397,搜「compile-time enforcement」)。 ↩ -
出处:「Chapter 1: Understanding TypeScript」第 139–145 段(
text/04-ch01-chapter-1-understanding-typescript.txt:139,搜「beyond its original scope」;:145,搜「most widely adopted and enduring」)。 ↩