《Programming TypeScript》核心摘要

作者: Boris Cherny | 让你的 JavaScript 应用规模化

关于作者:Boris Cherny

Boris Cherny 是一位杰出的工程和产品领导者,他的职业生涯横跨了大型社交媒体平台和前沿的人工智能大模型公司。他的经历充分展示了他在软件工程、产品战略以及对开发者社区(尤其是 TypeScript 社区)的深远影响。

职业经历摘要

  • Anthropic (2023年4月 - 至今):
    • 主任提示工程师 (Staff Prompt Engineer): 作为 Claude Code 的创造者,他领导开发了一款革命性的“智能体编码AI”(Agentic Coding AI)。该产品最初是一个为解决内部需求的“意外”项目,但迅速在公司内部普及,并成为 Anthropic 增长最快的产品之一。
    • 伟大事迹: 他将 Claude Code 从一个个人原型(最初用于自动化笔记和音乐播放)发展成为一个强大的编程工具。关键的突破在于赋予模型使用基础工具(如 Bash 命令行)的能力,这让模型“自发地”开始进行复杂的编码任务。在他的推动下,Anthropic 的技术人员入职时间从几周缩短到几天,因为新员工可以直接向 Claude Code 提问来学习代码库。
    • 职业生涯关键事件: 据 The Information 报道,在短暂离职并加入竞争对手 Anysphere(知名AI编程工具 Cursor 的开发者)仅仅两周后,他和同事 Cat Wu 被 Anthropic 成功返聘。这一事件凸显了他在公司编码AI战略中的核心地位和不可或缺的价值。
  • Meta (原 Facebook) (2016年 - 2023年):
    • 产品主管 (Product Lead) & 工程经理 (Engineering Manager): 在创作者货币化和公共内容等领域担任领导角色,拥有近8年的丰富经验。
  • Twitter (2014年 - 2016年):
    • 高级软件工程师 (Senior Software Engineer): 负责广告技术相关的前端工程。
  • O'Reilly Media (2018年 - 2019年):
    • 作者 (Author): 撰写了本书《Programming TypeScript》,成为 TypeScript 社区的重要参考资料。

产品与智能体编码哲学

  • 智能体编码 (Agentic Coding): 他认为软件开发的未来正从“直接操作文本”转向“向智能体描述意图”。开发者提出需求,AI 智能体负责规划步骤、使用工具(如文件读写、搜索)并执行,从而完成任务。
  • 产品跟随模型: 他的核心产品理念是,在AI领域,产品设计不应过于复杂,而应构建一个“最小化的界面”来跟上模型能力的快速迭代。应该为“6个月后的模型能力”而设计产品,而不是仅仅满足于当下。
  • 先解决自己的问题 (Dogfooding): Claude Code 的成功源于它首先解决了作者和 Anthropic 内部工程师的实际问题,其广泛的内部使用验证了产品的价值。

社区贡献与专业领域

  • 旧金山 TypeScript Meetup: 作为组织者,积极推动 TypeScript 社区的发展和知识分享。
  • 专业兴趣: 对编程语言、代码合成、静态分析以及构建优秀的用户体验充满热情。
  • 个人博客: 在 borischerny.com 上分享他的见解。

(信息根据作者的 LinkedIn 个人资料及公开访谈整理)

核心设计哲学与程序思维

本书的核心在于阐述 TypeScript 如何以一种极其务实的方式,帮助开发者构建更安全、更易于维护和扩展的 JavaScript 应用程序。

作者建议的设计哲学

  • 拥抱类型驱动开发 (Type-Driven Development):先草拟类型签名,再填充实现细节。这迫使你从高层次思考程序结构,从而设计出更简洁、更可靠的系统。
  • 实用性至上:TypeScript 的设计目标不是追求理论上的完美,而是解决 JavaScript 开发中的实际痛点,如恼人的 cannot read property '...' of undefined 错误。
  • 缩短反馈循环:TypeScript 的最大优势之一是在你编写代码时就提供类型检查和错误提示,而不是等到运行时才发现问题。这极大地提高了开发效率和代码质量。
  • 渐进式采用:TypeScript 被设计为可以与现有 JavaScript 代码库无缝协作,允许你逐步、按文件地迁移项目,而不是要求一次性重写。

推荐的编码风格与思维

  1. 尽可能让 TypeScript 推断类型避免不必要的显式类型注解。这能让代码更简洁,同时也能更好地暴露你可能没意识到的类型问题,而不是用显式类型“掩盖”它们。
  2. 拥抱不可变性 (Immutability)多使用展开语法 (...) 创建新对象或数组,而不是直接修改它们。这使得状态变化更可预测,调试也更简单。
  3. 编写可复用的泛型代码多态(Polymorphism),特别是通过泛型 (Generics) 实现,是你最好的朋友。它能让你编写出既灵活又类型安全的代码。
  4. 使用最新的 JavaScript 语法 (ESNext)这能让你的代码与标准保持一致,提高互操作性和可搜索性,并利用现代语言特性。
第一、二章:TypeScript 概览、编译器与类型系统

核心理念

  • 什么是 TypeScript? 它是一个 JavaScript 的超集,为其添加了静态类型系统。
  • 编译过程:TS 代码 → TSC (TypeScript 编译器) → 1. 解析为 AST → 2. 类型检查 → 3. 编译为 JS 代码 → JS 运行时执行。
  • 类型擦除 (Type Erasure):类型信息只存在于编译时,用于类型检查。编译生成的 JavaScript 代码中不包含任何类型信息,因此 TypeScript 对运行时性能没有负面影响

类型系统对比

  • JavaScript: 动态类型、弱类型、运行时检查。
  • TypeScript: 静态类型、强类型(大部分情况)、编译时检查、渐进类型 (Gradually Typed)结构化类型 (Structurally Typed)

结构化类型 (Structural Typing)

也称为“鸭子类型”。TypeScript 不关心一个值的“名义”类型(例如它是由哪个类创建的),只关心它的“形状”(它有哪些属性和方法)。如果一个值的结构与期望的类型兼容,那么它就是类型兼容的。

class Cat { meow() { console.log('meow') } }
class Dog { meow() { console.log('bark as meow') } }

function pet(animal: Cat) {
  animal.meow();
}

pet(new Cat()); // OK
pet(new Dog()); // 也 OK, 因为 Dog 的“形状”满足 Cat 的要求
第三章:深入理解类型

类型是“一组值以及你可以在这些值上执行的操作集合”。

关键类型

  • any: 类型系统的“教父”,应极力避免使用。它会关闭所有类型检查,让你回到不安全的 JavaScript 世界。
  • unknown: 更安全的 any。它代表任何值,但在你通过类型收窄(如 typeof 检查)证明其具体类型之前,你不能对它执行任何操作。
  • 基础类型: boolean, number, string, bigint, symbol, null, undefined
  • 字面量类型 (Literal Types): 一个值本身也可以作为一种类型,例如 type Status = 'success' | 'failure';。这是 TS 强大表达能力的基础。
  • 对象与数组:
    • 对象: 使用 { key: type } 描述其形状。? 表示可选属性,readonly 表示只读。
    • 数组: number[]Array<number>
    • 元组 (Tuples): 固定长度和固定成员类型的数组,如 [string, number]
  • enum (枚举): 作者建议谨慎使用或避免使用,因为数字枚举存在一些不安全的行为。字符串枚举相对更安全。

类型操作符

  • 类型别名 (Type Alias): type MyString = string;
  • 联合类型 (Union Types): string | number (表示“或”)
  • 交叉类型 (Intersection Types): TypeA & TypeB (表示“与”,合并两个类型的成员)
第四章:函数

函数类型签名

函数的参数类型通常需要显式注解,而返回类型 TypeScript 大多可以自动推断。

// 完整的调用签名
type Log = (message: string, userId?: string) => void;

重要概念

  • 可选参数和默认参数: function log(msg: string, user = 'guest') { ... }
  • 重载 (Overloads): 为一个函数提供多个不同的调用签名,以处理不同的参数组合。实现函数本身需要一个能兼容所有重载签名的通用签名。
  • this 的类型: 为了安全地使用 this,应在函数的第一个参数位置声明它的类型:function say(this: MyType, message: string) { ... }
  • 多态 (Polymorphism) / 泛型 (Generics):

    这是 TypeScript 最强大的功能之一,用于编写可处理多种类型的、可复用的代码。使用尖括号 <T> 来声明一个泛型类型参数。

    function map<T, U>(array: T[], f: (item: T) => U): U[] {
      // ...
    }
第五章:类与接口

核心特性

  • 类 (Classes): 支持访问修饰符 (public, protected, private)、abstract 抽象类、readonly 属性等。
  • 接口 (Interfaces): 仅用于描述类型的“形状”,在编译后会被擦除。

interface vs. type

  • 接口是“开放的”:同名的多个接口会自动合并(声明合并 Declaration Merging)。这对于扩展第三方库的类型非常有用。
  • 类型别名是“封闭的”:不能重复声明,但更通用,可以表示联合类型、元组等任何类型。
  • 作者建议:当描述一个对象或类的形状时,优先使用 interface;其他情况使用 type

高级模式

  • Mixins: 通过函数返回类的模式,来模拟多重继承,为类混入多种行为。
  • Decorators: 实验性功能,提供一种元编程的语法糖来修改类、方法或属性。
第六章:高级类型

类型关系

  • 子类型 (Subtyping): 如果类型 BA 的子类型 (B <: A),那么任何需要 A 的地方都可以安全地使用 B
  • 型变 (Variance):
    • 协变 (Covariance): 大多数类型(如对象属性、函数返回值)都是协变的。如果 Cat <: Animal,那么 Cat[] <: Animal[]
    • 逆变 (Contravariance): 函数参数是逆变的。如果 Cat <: Animal,那么 (animal: Animal) => void(cat: Cat) => void 的子类型。
  • 类型拓宽 (Type Widening): 当使用 letvar 声明变量时,TypeScript 会推断一个更通用的类型。而使用 const 声明时,会推断出最窄的字面量类型。可以使用 as const 来强制进行最窄推断。

高级类型操作

  • 类型收窄 (Type Narrowing / Refinement): 通过 if, typeof, instanceof 等控制流分析,TypeScript 可以在代码块内将一个宽泛的类型“收窄”为一个更具体的类型。
  • 可辨识联合类型 (Discriminated Unions): 一种强大的模式,通过给联合类型中的每个成员添加一个共同的、具有字面量类型的字段(“标签”),可以实现完美的类型收窄和穷尽性检查。
    type Success = { type: 'success', response: ... };
    type Failure = { type: 'failure', error: ... };
    type Result = Success | Failure;
    
    function handle(r: Result) {
      if (r.type === 'success') {
        // r 在这里被收窄为 Success 类型
      } else {
        // r 在这里是 Failure 类型
      }
    }
  • keyof T: 获取类型 T 的所有键,结果是一个联合类型。
  • T[K] (索引访问类型): 获取类型 T 中键 K 对应的属性类型。
  • 映射类型 (Mapped Types): 基于一个已有类型创建新类型,例如 { [K in keyof T]: T[K] }Partial<T>, Readonly<T> 等内置工具类型就是用它实现的。
  • 条件类型 (Conditional Types): T extends U ? X : Y。它允许类型根据条件进行选择,并且当 T 是联合类型时,具有分配特性 (Distributive)
  • infer 关键字: 在条件类型中,可以动态地推断出一个新的类型变量。
第七、八章:错误处理与异步编程

错误处理模式

  1. 返回 null: 简单,但丢失了错误信息。
  2. 抛出异常 (Throwing Exceptions): 强大,但类型签名中不体现,破坏了类型安全。
  3. 返回异常 (Returning Exceptions): function(): Result | Error。类型安全,强制调用者处理错误,但链式调用会变得冗长。
  4. Option 类型: 函数式编程模式,将结果包装在 Some(value)None 容器中,可以优雅地进行链式调用。

异步编程

  • 回调函数 (Callbacks): 基础,但容易导致“回调地狱”。
  • Promises: 现代 JavaScript 异步标准,通过 .then().catch() 解决回调地狱,使异步代码更易于组合和管理。
  • async/await: Promise 的语法糖,让你可以用同步的方式编写异步代码,极大提高了可读性。
  • 类型安全的事件发射器: 可以使用映射类型来创建一个协议,精确地为每个事件名称定义其负载类型。
第九章至十二章:框架、JavaScript 互操作与构建

与 JavaScript 互操作

  • 声明文件 (`.d.ts`): 是 TypeScript 与 JavaScript 库协作的基石。它只包含类型信息,不包含实现。
  • DefinitelyTyped: 一个庞大的社区维护的声明文件仓库。大多数流行的 JS 库都可以通过安装 @types/<package-name> 来获得类型支持。
  • 渐进式迁移策略:
    1. tsconfig.json 中启用 allowJs: true
    2. (可选)启用 checkJs: true 并使用 // @ts-check 或 JSDoc 注解来为 JS 文件添加类型检查。
    3. 逐个将 .js 文件重命名为 .ts.tsx 并修复类型错误。
    4. 最终开启 strict: true 模式,实现完全的类型安全。

模块与构建

  • 模块系统: 始终优先使用 ES 模块 (import/export)。命名空间 (namespace) 是一种较旧的、不推荐的模式。
  • tsconfig.json 核心配置:
    • target: 指定编译输出的 JavaScript 版本(如 "es2015")。
    • module: 指定输出的模块规范(如 "commonjs" 用于 Node.js,"esnext" 用于现代打包工具)。
    • lib: 指定项目中可用的运行时库(如 "dom" 用于浏览器环境,"es2020" 用于 JS 新特性)。
  • 项目结构: 推荐将源码放在 src/ 目录,编译输出到 dist/ 目录。

原文

源链接

附件

中文PDF (文件不存在)

下载

中文epub (文件不存在)

下载