跳转至

只有 TypeScript 有的特性 — 参数属性、枚举、命名空间与「JS with types」

这一章讲一件事: TypeScript 有三样自己会生成运行时代码的特性——类参数属性、 枚举(enum)、命名空间(namespace)。它们不属于 JavaScript,编译出的 JS 里却真实存在。 这是全书第一次正面处理「TypeScript 与 JavaScript 的关系」这个哲学问题: 结论是——今天是「JavaScript with types」的时代,这三样是遗产。

1. 为什么单独开一章:第 03 章那条原则的例外清单

第 03 章立过一条指导原则:TypeScript 是 JavaScript 之上的薄语法层, 不改运行行为。本章开头就纠正:「有几个例外」1。

  薄语法原则(第 03 章)        例外(本章)
  ─────────────────────────    ──────────────────────────
  类型注解 —— 编译后消失        参数属性 —— 生成 this.xxx 赋值
  接口/类型别名 —— 完全擦除      枚举 enum —— 生成一个真实的对象
  推断 —— 无运行痕迹            命名空间 —— 生成一个真实的对象

  图说:右边三样都「有运行时后果」,所以被单列出来审问。
  TypeScript 5.8 的 --erasableSyntaxOnly 开关甚至可以直接把它们禁用[^2]。

审问的标准很清楚:一个特性如果今天才提出,还会被接受吗? 对这三样的答案都是「不会」——但 TypeScript 永远不会移除它们(破坏的存量代码太多), 所以问题变成「新代码该不该用」3。

2. 类参数属性:省掉的代码,换来的不透明

解决什么问题: 类构造器里「收参数 → 赋给属性」的样板代码太啰嗦:

class Rating {
  value: number;
  max: number;

  constructor(value: number, max: number) {
    this.value = value;
    this.max = max;
  }
}

参数属性(parameter properties)把这六行塌缩成一行——在构造器参数前挂可见性修饰符, 参数自动变成同名属性:

class Rating {
  constructor(public value: number, private max: number) {}
}

public value 的意思是:「这是一个参数,同时声明一个 public 属性 value 并赋上它」。 编译产物就是手写的那个六行版本4。

省代码是真的,代价也是真的,原书点了两条:

  1. 生成的 JavaScript 不再是源码的直译。 不看编译产物的人,很难猜出这个空构造器 类居然有属性——「不熟悉这个特性就看不懂发生了什么」5。
  2. 它是「薄语法」原则的直接违反者:别的 TS 特性编译后消失,它编译后长出代码。

实用立场:在满是有经验 TypeScript 开发者的代码库里可以用(人人都看得懂); 教学和跨语言团队里,手写赋值更透明。

一个参数属性的判断练习:看到它该想什么

遇到 constructor(public value: number, private max: number) {} 这个空壳构造器, trained 的眼睛应该按顺序完成三个动作:

  ① 认修饰符:参数前有 public/private/protected → 这是参数属性,不是普通参数
  ② 补脑产物:每个带修饰符的参数 = 一个同名属性 + 构造器里的一次赋值
     → 这个类实际有两个实例属性:value(公开)和 max(私有)
  ③ 找对照:想知道属性清单,别读构造器——悬停类名或在类内输入 this. 让补全说话

  图说:三步对应「认特征 → 推语义 → 用工具验证」,第 ③ 步永远是对的答案。

第三步值得单独强调:参数属性造成的「源码和直觉不符」,恰好是悬停和补全的 用武之地——凡是编译器替你生成的成员,就让编译器自己报出来。 这条原则在 enum、namespace 上同样适用(它们的成员清单也不总写在明面上)。

3. 枚举(enum):一张 API 表,三个怪癖

解决什么问题: 一组相关但不多的常量值,「有限的、不期望变化的一组」—— 比如唱片状态。enum 把它们收成一组命名常量,既能当类型用,也能当值用6:

enum AlbumStatus {
  NewRelease,  // 0
  OnSale,      // 1
  StaffPick,   // 2
}

数字枚举从 0 开始自动编号;可以显式指定(NewRelease = 1),指定一个后 后面的自动递增(1、2、3)7。字符串枚举给每个成员指定字符串值:

enum AlbumStatus {
  NewRelease = "NEW_RELEASE",
  OnSale = "ON_SALE",
  StaffPick = "STAFF_PICK",
}

数字枚举的编号规则值得单独收一张小表,三种写法经常混在一行里:

写法 结果 依据
全不指定 NewRelease=0、OnSale=1、StaffPick=2 从 0 起自动编号
全指定 NewRelease=1、OnSale=2、StaffPick=3 显式值原样采用
只指定第一个 NewRelease=1、OnSale=2、StaffPick=3 从最后一个指定值起递增

第三种最容易踩坑:NewRelease = 1 之后,OnSale 和 StaffPick 不是回到 0 重新数, 而是接着 1 往后排7。如果某个成员的数值有协议含义(要和后端、存储格式对齐), 全部显式写出——半自动编号在插入新成员时会悄悄改变后续成员的数值。

当类型用时,参数只收枚举成员:logStatus(AlbumStatus.NewRelease) ✅; 数字枚举还放行普通数字(logStatus(0) ✅,3 ❌),字符串枚举不放行裸字符串8。 这些是 API 表面;翻编译产物,三个怪癖浮出水面。

怪癖一:数字枚举的反向映射

数字枚举编译出来不是一个简单的「名 → 值」对象,而是双向的:

// 编译产物(整理后)
var AlbumStatus = {
  0: "NewRelease",
  1: "OnSale",
  2: "StaffPick",
  NewRelease: 0,
  OnSale: 1,
  StaffPick: 2,
};

值也能当键用:AlbumStatus[0] 是 "NewRelease"9。后果是个著名的坑:

Object.keys(AlbumStatus);
// ["0", "1", "2", "NewRelease", "OnSale", "StaffPick"]
//  ← 键和值全在里面,是键数量的两倍!

遍历枚举时如果不防备,数字键会混进来——原书原话:「这可以是个真 gotcha」10。 字符串枚举没有反向映射,编译产物就是单向的「名 → 值」11。 同一个 enum 关键字,两种底层行为——这是不一致性的第一个来源。

插曲:那段「看不懂的」编译产物到底在干什么

原书把数字枚举的原始编译产物完整摆了出来,第一次看像乱码,拆开行走一遍就服了:

var AlbumStatus;
(function (AlbumStatus) {
  AlbumStatus[(AlbumStatus["NewRelease"] = 0)] = "NewRelease";
  AlbumStatus[(AlbumStatus["OnSale"] = 1)] = "OnSale";
  AlbumStatus[(AlbumStatus["StaffPick"] = 2)] = "StaffPick";
})(AlbumStatus || (AlbumStatus = {}));

一行一行读:外层的函数表达式立刻自执行,拿到(或新建)AlbumStatus 这个对象; 中间每一行做两次赋值——AlbumStatus["NewRelease"] = 0 建立正向映射, 紧接着 AlbumStatus[0] = "NewRelease" 用刚才的返回值(0)当键,建立反向映射。 一行代码、两个方向、一举两得——反向映射不是额外特性,是这种写法免费附赠的副产品9。 看懂这段代码的价值不在读懂本身,而在确认一件事:enum 的怪癖全部是 「编译器替你生成的运行时代码」造成的,而不是类型系统的发明; 这条线索直接通向本章的总立场(第 5 节)。

怪癖二:名目类型 vs 结构化类型

这是三怪癖里最 type-theory 的一个。TypeScript 的默认比较规则是结构化的: 两个类型结构相同即视为同一类型(第 07 章的 interface 和 type 可以互换使用,根源在此)。 字符串枚举是唯一的例外——它是名目(nominal)比较的:看名字,不看结构。

enum AlbumStatus {
  NewRelease = "NEW_RELEASE",
  /* ... */
}

enum AlbumStatus2 {          // 成员一模一样
  NewRelease = "NEW_RELEASE",
  /* ... */
}

logStatus(AlbumStatus2.NewRelease);
// ❌ Argument of type 'AlbumStatus2' is not assignable to parameter of type 'AlbumStatus'.

成员值完全相同,仅仅枚举名字不同,就是两个不可互换的类型12。 数字枚举反而接近结构化(普通数字能进去),字符串枚举却名目化—— 同一个关键字两种比较语义,这是不一致性的第二个来源。 对习惯了结构化思维的 TypeScript 用户,这是惊喜;对从 C#/Java 来的开发者, 这才是「枚举本来就该有的样子」13。

怪癖三:const enum 的消失术

const enum 在普通枚举前加 const:

const enum AlbumStatus {
  NewRelease = "NEW_RELEASE",
  /* ... */
}

编译时枚举对象整个消失,所有引用处直接内联成字面值:

// let albumStatuses = [AlbumStatus.NewRelease, ...];
// 编译后:
let albumStatuses = ["NEW_RELEASE", "ON_SALE", "STAFF_PICK"];

零运行时成本,但代价是:别的代码(尤其是声明文件的消费方)拿不到这个枚举对象, 行为会出人意料。TypeScript 团队建议库里不要用 const enum14。

「行为出人意料」具体指什么?原书给了线索:const enum 在声明文件(.d.ts)里 有额外限制——声明文件的消费方常常只能拿到内联后的字面值,而拿不到枚举对象本身, 对不上账时排查极难。这也是「库里别用」这条建议的针对性所在: 库的类型恰恰住在声明文件里,而 const enum 在那里最不稳定14。

该用枚举吗?原书把答案推迟了一章

枚举的替代品是字面量并集 + as const(第 08 章的工具):

const albumStatus = {
  newRelease: "NEW_RELEASE",
  onSale: "ON_SALE",
  staffPick: "STAFF_PICK",
} as const;

type AlbumStatus = (typeof albumStatus)[keyof typeof albumStatus]; // "NEW_RELEASE" | "ON_SALE" | "STAFF_PICK"

零编译魔法(就是一个普通对象 + 类型推导)、行为完全可预测、没有反向映射和名目比较。 这个对照的完整版在下一章(第 11 章的 as const 小节)兑现;此处先记住结论方向: 新代码优先 as const 对象,枚举留给维护存量15。

4. 命名空间(namespace):前模块时代的组织术

解决什么问题: TypeScript 诞生的 2010 年,JavaScript 还没有模块系统—— 一切声明都落在全局作用域,名字撞车是日常。namespace 是 TypeScript 当时的答案: 造一个「迷你模块」,内部的东西要 export 才外露16:

namespace RecordStoreUtils {
  export namespace Album {
    export interface Album {
      title: string;
      artist: string;
      year: number;
    }
  }

  export function addAlbum(title: string, artist: string, year: number) { /* ... */ }

  export namespace Sales {
    export function recordSale(/* ... */) { /* ... */ }
    export function calculateTotalSales(albumTitle: string): number { /* ... */ }
  }
}

编译产物和 enum 同款套路:一个真实对象,export 的成员挂上去17。 把原始产物也看一眼,模式和 enum 那个 IIFE 完全同构:

var RecordStoreUtils;
(function (RecordStoreUtils) {
  function addAlbum(title, artist, year) { /* ... */ }
  RecordStoreUtils.addAlbum = addAlbum;
})(RecordStoreUtils || (RecordStoreUtils = {}));

函数在 IIFE 内部定义,再手动挂到对象上——namespace 和 enum 共用同一套 「造对象 → 自执行 → 挂成员」的编译模板。这就是为什么本章把它们归为同类: 它们都是编译器替你生成的运行时代码,连生成手法都一模一样17。

用法上,namespace 的点号路径同时是值的路径(调函数)和类型的路径(标注接口):

const odelay: RecordStoreUtils.Album.Album = { title: "Odelay!", artist: "Beck", year: 1996 };
RecordStoreUtils.Sales.recordSale("Odelay!", 1, 10.99);

RecordStoreUtils.Album.Album 这种三层点名初看滑稽(对象.子空间.接口), 其实是「值世界里嵌套对象」和「类型世界里点路径」的叠加—— 类型版 namespace 的全部意义,就藏在这种「用点号组织类型命名空间」的能力里16。

声明合并,再一次

第 07 章在 interface 上见过声明合并;namespace 也会:

namespace RecordStoreUtils {
  export namespace Album { /* ... */ }
}

namespace RecordStoreUtils {
  export namespace Sales { /* ... */ }
}
// 两个声明自动合并成一个 RecordStoreUtils,Album 和 Sales 都在

连 namespace 里的 interface 也会合并18。这正是第 07 章那个 gotcha 的「好理由」: 声明合并是给全局作用域打补丁的正规通道——在全局类型文件里(第 14 章的专题), 你可以「再声明一次」给已有的全局对象加类型,而不是改写别人的文件。 namespace 把这个能力做成了语言特性。

该用命名空间吗:运行时版进博物馆,类型版还有一口气

原书的回答分两层:

运行时 namespace(带函数实现的那种)——别用了。 ES 模块(import/export) 已经普及,它全方位更好;「runtime namespaces are a thing of the past」是原书的原话19。

类型版 namespace(只装 type 的那种)——还有正当场景。 它编译后什么都不剩, 作用是给一组相关类型起个公共前缀(DB.User 而不是到处 import), 在全局类型和声明文件里有用武之地20。

两个形态并排看一眼,差别立刻具体:

// 运行时版:编译出一个真对象,log 是个能调的函数
namespace Utility {
  export function log(message: string) {
    console.log(message);
  }
}

// 类型版:编译后什么都没有,只是给类型起个带前缀的名字
namespace DB {
  export type User = {
    id: number;
    name: string;
  };
}

const user: DB.User = { id: 1, name: "Alice" }; // DB 只在类型位置出现

判别法一行:namespace 里只有 type 导出 = 类型版(无害的命名习惯); 有值导出(函数、常量)= 运行时版(该迁移到 ES 模块)。 类型版之所以「还有一口气」,是因为它压根不违反「薄语法」原则——它擦除得干干净净, 和前缀式的类型组织是同一个东西20。

这里也顺手回答一个常见疑问:类型版 namespace 和普通模块里的 export type 有什么差别? 功能上几乎没有——DB.User 和 import type { User } from "./db" 是两种等价的组织方式。 真正的差别在使用姿势:namespace 前缀适合「类型按主题分区、被很多文件稀疏引用」 的场景(全局类型文件尤甚),import 适合「类型跟着实现走」的模块世界。 第 14 章会再见到它:declare namespace 是向全局作用域注入成组类型的主力, 那也是类型版 namespace 今天最正当的岗位。

5. 总立场: relics,但有豁免条款

三个特性有个共同画像:2010 年的 TypeScript 觉得 JavaScript 需要被修, 于是自己加了运行时特性;2026 年的 TypeScript 认定自己是「JavaScript with types」, 不再越俎代庖。 TypeScript 团队的项目经理 Daniel Rosenwasser 本人就是 TC39 (JavaScript 的标准委员会)的联合主席——这个人事安排本身就是宣言: 要加语言特性,去 JavaScript 那里加,别在 TypeScript 里加21。

原书给的使用政策,值得整段记住:

  • 这三样永远不会被移除——破坏的存量代码太多,所以「继续用是安全的」;
  • 但新代码别再用——「如果它们今天才被提出,根本不会过审」;
  • 存量代码库如果已经大量在用,不值得专门清除;团队达成一致、保持一致,才是关键3。

而语言本身也往这个方向走:TypeScript 5.8(2025 年初)加了 --erasableSyntaxOnly 开关——打开后,enum、namespace、参数属性直接报错, 只允许「可完全擦除」的语法。旗帜鲜明地把这三样标记为「非未来的部分」2。

这场转向的来龙去脉,值得按年份重讲一遍

原书给了一段简明的技术史,把「为什么是 relics」讲透了:

  • 2010 年的氛围:JavaScript 被视为一门「需要被修」的语言。 新运行时特性被认为理所当然地是好事,enum、namespace、参数属性都诞生在这种氛围里21。
  • 中间发生的事:JavaScript 自身变健康了。TC39——决定 JavaScript 加什么特性的标准委员会——变得活跃而高效,语言以每年一批新特性的速度演进21。
  • TypeScript 团队角色重定位:不再「替 JavaScript 加特性」,而是贴着 JavaScript 走。 人事安排是这条宣言的注脚:TypeScript 项目经理 Daniel Rosenwasser 本人就是 TC39 的联合主席——要加语言特性,去 JavaScript 那里加21。
  • 结晶成一句话:今天理解 TypeScript 的正确姿势是 「JavaScript with types」 (带着类型的 JavaScript)——原书把这句当作全章的判词21。
  • 立法:--erasableSyntaxOnly 把「erasable(可完全擦除)」立成了正式标准: 类型标注能整个剥掉、不影响运行,这才是 TypeScript 认可的语法; 需要复杂转换、会生成运行时代码的特性,被划到线的另一边2。

把这条线串起来看,三个特性不是「不好用」才被劝退,而是选错了诞生的时代: 它们解决的是 2010 年 JavaScript 的真问题(没有模块、常量组织简陋), 而 JavaScript 自己把这些问题解决得更好之后,它们的存续理由就只剩兼容。

判断(我们的,不是书里的): 这一章是全书「语言演化观」含量最高的一章, 它对编程语言研究的价值超过它的实用价值:三个特性是同一场实验的三份样本—— 「一门转译语言该不该给宿主语言加运行时扩展?」TypeScript 用十五年得出的答案是 「不该,而且要把当年的决定一个个收回」。--erasableSyntaxOnly 是这场收回运动的 正式立法。读别的转译语言/超集(早期的 CoffeeScript、未来的某个 X-with-types)时, 这一章的整个论证结构都可以复用。 如果错,会错在: 如果 JavaScript 本身长期不吸收某些本该有的特性 (比如正式的枚举),而 TypeScript 的遗产特性恰好填了这个坑, 「别用 enum」的建议会让一部分项目错失顺手的工具——但截至 2026 年,TC39 的活跃程度 让这个风险很低;而且 as const 方案在绝大多数场景确实等价。

6. 迁移手册:三个 relic 各自怎么换

「别用」之后的下一个问题自然是「那用什么」。三个特性各有一条主流替代路线, 都已经在前文出现过,这里汇总成一张对照表,方便存量代码迁移时逐处替换:

遗产特性 替代路线 代价
类参数属性 constructor(public value) 显式声明属性 + 构造器里手动赋值(就是它的编译产物) 多两三行,换来「你写的」和「运行的」逐字一致
数字/字符串枚举 as const 对象 + 类型推导(第 11 章的 Direction 模式) 失去「类型恰好是这组值」的名义性;多数场景结构化的字面量并集本来就够
const enum 同上,as const 对象连内联优化都近似(打包器会对常量做同样的事) 失去官方内联,但对库消费方的怪异行为一并消失
运行时 namespace ES 模块:一个文件一个模块,import/export 组织 文件变多;换来的是行业标准工具链的全部支持
类型版 namespace 普通模块 + import type;确实需要全局类型时用 declare namespace(第 14 章) 多数场景直接消失——类型本来就该跟着 import 走

两个迁移时的注意项。第一,别混用旧新两界:同一个项目里 enum 和 as const 并存 没问题,但同一个概念别两边都建——两套「状态枚举」是比任何一种更糟的状态。 第二,迁移的优先级由接触频率决定:被 import 最多的那个遗产类型先迁, 它每被一处引用,替代方案的收益就兑现一次(第 17 章复杂度预算的同一本账)。

判断(我们的,不是书里的): 这张表对「存量项目怎么办」比原书的立场条款 更可操作——原书给的是政策(别用、可留、保持一致),我们补的是路线(每个 relic 换到哪、花什么代价)。政策告诉你方向,路线才让你能在下个迭代动手。 如果错,会错在: 对「类型版 namespace」的处置可能过于激进——在大型全局类型 文件(如全局事件映射)里,namespace 前缀提供的「点路径命名」确实比一长串 import 更易读;若你的项目大量使用这种模式,它不算 relic,算方言。

7. 作者的判断与证据(分开标)

书里有据的:

  • 参数属性的编译产物与「非直译」的代价——代码对照在案45。
  • 数字枚举反向映射与 Object.keys 的坑——编译产物和实际输出在案910。
  • 字符串枚举的名目比较——双枚举对照实验在案12。
  • 团队建议库里避免 const enum——原书明写「The TypeScript team actually recommends avoiding const enums in the library code」14。
  • 运行时 namespace 已过时——「a thing of the past」原文可考19。
  • erasableSyntaxOnly 的含义与指向——5.8 版本号在案2。

原书没说的(我们补):

  • 枚举在学术类型论里的定位(名目类型 vs 结构化类型的经典对照)——补充(来自通用知识): 名目类型是 Java、C# 的传统,结构化是 ML 系的传统;TypeScript 以结构化为默认、 唯独字符串枚举名目化,是「向 OO 开发者示好」的历史妥协。
  • CoffeeScript 等前辈的类似教训——补充(来自通用知识),用于支撑判断块里的跨语言类比。

8. 边界与局限

  • 本章对枚举的替代方案(as const 对象)只给了预告,完整推导(typeof、keyof、索引访问)是第 11 章的内容;现在照抄即可,不必理解。
  • 参数属性在 Angular 等框架代码里仍然大量存在(装饰器构造器注入),「别用」主要适用通用应用代码。
  • namespace 的全局类型补丁用法(本章只点了一句)在第 14 章展开前,不要自行模仿——那是声明文件的地盘。
  • erasableSyntaxOnly 是 5.8 的新开关,老项目升级 TypeScript 版本时要留意它的默认关闭状态。

9. 可带走的

  1. TypeScript 有三个生成运行时代码的特性:参数属性、enum、namespace——它们是「薄语法」原则的例外。
  2. 参数属性 constructor(public value: number) 省六行,但编译产物不透明;熟手团队可用。
  3. 数字枚举有反向映射,Object.keys 会返回键和值两套——遍历前想清楚。
  4. 字符串枚举是名目比较(看名字),和 TypeScript 的结构化默认格格不入;数字枚举反而放行普通数字。
  5. const enum 编译后消失、值被内联;库里别用。
  6. 枚举的现代化替代:as const 对象 + 字面量并集(第 11 章给完整推导)。
  7. 运行时 namespace 已进博物馆;ES 模块全面取代。
  8. 类型版 namespace(只装 type)在全局类型文件里仍有正当用途。
  9. 新代码政策:永远不被移除 ≠ 值得新用;存量豁免,新码绕行。
  10. --erasableSyntaxOnly(TS 5.8+)可以把这三样直接禁用——语言官方的「非未来」标记。

10. 原文地图

主题 原书位置 原文位置
本章的定位(薄语法的例外) Class Parameter Properties text/25-fm-class-parameter-properties.txt:8(搜「runtime features」)
参数属性与编译产物 同上 text/25-fm-class-parameter-properties.txt:19(搜「public value」)
参数属性的不透明代价 同上 text/25-fm-class-parameter-properties.txt:33(搜「not a direct representation」)
枚举的定义与定位 同上 text/25-fm-class-parameter-properties.txt:37(搜「named constants」)
数字枚举自动编号 同上 text/25-fm-class-parameter-properties.txt:53(搜「would be 0」)
只指定首值的自动递增规则 同上 text/25-fm-class-parameter-properties.txt:79(搜「automatically increment」)
enum 反向映射 IIFE 原文 同上 text/25-fm-class-parameter-properties.txt:126(搜「})(AlbumStatus」) · text/25-fm-class-parameter-properties.txt:123(搜「AlbumStatus["NewRelease"] = 0」)
namespace 编译产物原文 同上 text/25-fm-class-parameter-properties.txt:324(搜「var RecordStoreUtils」) · text/25-fm-class-parameter-properties.txt:330(搜「})(RecordStoreUtils」)
2010 年氛围与 TC39 转向 同上 text/25-fm-class-parameter-properties.txt:437(搜「needed fixing」) · text/25-fm-class-parameter-properties.txt:441(搜「co-chair」)
显式值与自动递增 同上 text/25-fm-class-parameter-properties.txt:79(搜「auto」)
数字枚举的反向映射 同上 text/25-fm-class-parameter-properties.txt:128(搜「reverse mapping」)
Object.keys 的坑 同上 text/25-fm-class-parameter-properties.txt:143(搜「Object.keys」)
字符串枚举编译产物 同上 text/25-fm-class-parameter-properties.txt:160(搜「NEW_RELEASE」)
数字枚举放行普通数字 同上 text/25-fm-class-parameter-properties.txt:191(搜「logStatus(0)」)
字符串枚举不放行裸字符串 同上 text/25-fm-class-parameter-properties.txt:194(搜「not assignable」)
名目比较(AlbumStatus2 实验) 同上 text/25-fm-class-parameter-properties.txt:223(搜「AlbumStatus2」)
结构化 vs 名目 同上 text/25-fm-class-parameter-properties.txt:213(搜「structurally」)
const enum 内联消失 同上 text/25-fm-class-parameter-properties.txt:251(搜「albumStatuses = [」)
库里避免 const enum 同上 text/25-fm-class-parameter-properties.txt:253(搜「avoiding const enums」)
枚举替代方案的预告 同上 text/25-fm-class-parameter-properties.txt:261(搜「page 203」)
namespace 的历史定位 同上 text/25-fm-class-parameter-properties.txt:265(搜「lack of a module system」)
namespace 编译产物 同上 text/25-fm-class-parameter-properties.txt:332(搜「accessed as an object」)
namespace 声明合并 同上 text/25-fm-class-parameter-properties.txt:367(搜「automatically combined」)
「运行时 namespace 属于过去」 同上 text/25-fm-class-parameter-properties.txt:412(搜「thing of the past」)
类型版 namespace 同上 text/25-fm-class-parameter-properties.txt:425(搜「Type-only」)
「JS with types」的定位 同上 text/25-fm-class-parameter-properties.txt:443(搜「JavaScript with types」)
「今天提出不会被考虑」 同上 text/25-fm-class-parameter-properties.txt:445(搜「would not even be considered」)
存量豁免政策 同上 text/25-fm-class-parameter-properties.txt:447(搜「never stop supporting」)
erasableSyntaxOnly 同上 text/25-fm-class-parameter-properties.txt:455(搜「erasableSyntaxOnly」)
本章小结 同上 text/25-fm-class-parameter-properties.txt:461(搜「compile to JavaScript」)


  1. 出处:「Class Parameter Properties」第 6–10 段(text/25-fm-class-parameter-properties.txt:8,搜「runtime features」)。 ↩

  2. 出处:「Class Parameter Properties」第 453–457 段(text/25-fm-class-parameter-properties.txt:455,搜「erasableSyntaxOnly」)。 ↩↩↩

  3. 出处:「Class Parameter Properties」第 445–451 段(text/25-fm-class-parameter-properties.txt:445,搜「would not even be considered」);存量豁免见第 447 段(搜「never stop supporting」)、团队一致性见第 451 段(搜「staying consistent」)。 ↩↩

  4. 出处:「Class Parameter Properties」第 14–31 段(text/25-fm-class-parameter-properties.txt:19,搜「public value」)。 ↩↩

  5. 出处:「Class Parameter Properties」第 33 段(text/25-fm-class-parameter-properties.txt:33,搜「not a direct representation」)。 ↩↩

  6. 出处:「Class Parameter Properties」第 35–53 段(text/25-fm-class-parameter-properties.txt:37,搜「named constants」)。 ↩

  7. 出处:「Class Parameter Properties」第 65–85 段(text/25-fm-class-parameter-properties.txt:79,搜「automatically increment」)。 ↩↩

  8. 出处:「Class Parameter Properties」第 170–211 段(text/25-fm-class-parameter-properties.txt:191,搜「logStatus(0)」)、第 208 段(搜「not assignable」)。 ↩

  9. 出处:「Class Parameter Properties」第 109–144 段(text/25-fm-class-parameter-properties.txt:128,搜「reverse mapping」)。 ↩↩↩

  10. 出处:「Class Parameter Properties」第 143 段(text/25-fm-class-parameter-properties.txt:143,搜「Object.keys」)。 ↩↩

  11. 出处:「Class Parameter Properties」第 146–168 段(text/25-fm-class-parameter-properties.txt:165,搜「only the keys」)。 ↩

  12. 出处:「Class Parameter Properties」第 213–226 段(text/25-fm-class-parameter-properties.txt:223,搜「AlbumStatus2」)。 ↩↩

  13. 出处:「Class Parameter Properties」第 211 段(text/25-fm-class-parameter-properties.txt:211,搜「C# and Java」)。 ↩

  14. 出处:「Class Parameter Properties」第 253 段(text/25-fm-class-parameter-properties.txt:253,搜「avoiding const enums」)。 ↩↩↩

  15. 出处:「Class Parameter Properties」第 255–261 段(text/25-fm-class-parameter-properties.txt:261,搜「page 203」)。 ↩

  16. 出处:「Class Parameter Properties」第 263–310 段(text/25-fm-class-parameter-properties.txt:267,搜「mini-modules」)。 ↩↩

  17. 出处:「Class Parameter Properties」第 312–332 段(text/25-fm-class-parameter-properties.txt:332,搜「accessed as an object」)。 ↩↩

  18. 出处:「Class Parameter Properties」第 334–404 段(text/25-fm-class-parameter-properties.txt:367,搜「automatically combined」)。 ↩

  19. 出处:「Class Parameter Properties」第 406–431 段(text/25-fm-class-parameter-properties.txt:412,搜「thing of the past」)。 ↩↩

  20. 出处:「Class Parameter Properties」第 414–431 段(text/25-fm-class-parameter-properties.txt:427,搜「export type User」)。 ↩↩

  21. 出处:「Class Parameter Properties」第 433–451 段(text/25-fm-class-parameter-properties.txt:441,搜「co-chair」)。 ↩↩↩↩↩