01|如果第二篇讲的是 TypeScript 为什么能进场,那第三篇要回答的就是:为什么当年看起来同样很能打的 Flow,最后却没有把这场仗打成自己的
如果你是后来的前端开发者,
今天回头看类型史,很容易产生一种错觉:
好像 TypeScript 一出来,
事情就已经差不多定了。
可真实历史并不是这样。
在 TypeScript 真正长成今天这种“几乎默认”的位置之前,
JavaScript 世界里其实有过一个非常像样、甚至一度在很多人眼里更“贴身”的对手:
Flow。
而且它不是那种边缘试验品。
它有几层特别强的初始优势:
- 它来自 Facebook
- 它和 React 语境天然更近
- 它一开始就把自己讲成“别丢掉 JavaScript feel”
- 它强调强分析、强推断、后台实时检查
更重要的是,
它出现的时间点也很有意思。
2014 年 Facebook 正式开源 Flow 时,
React 的影响力正在迅速上升,
前端社区也越来越认真地面对大型 JavaScript 应用的维护问题。
所以第三篇最值得讲的,绝不是:
“Flow 不行,所以输了。”
恰恰相反。
第三篇真正要讲的是:
Flow 当年不是不强,而是它最后输掉的,并不是单一的类型能力,而是整场生态入口之争。
02|Flow 最早看起来很有希望,不只是因为它来自 Facebook,而是因为它在姿态上特别会讨 JavaScript 世界喜欢
Facebook 2014 年发布 Flow 时,官方介绍里有两句特别值得注意。
一句是:
Flow is a new static type checker for JavaScript
另一句是:
we have designed Flow so developers can reap its benefits without losing the “feel” of coding in JavaScript
这两句放一起看,你会立刻发现它的路数非常聪明。
因为它其实在同时回应两边需求:
一边回应大型应用的现实压力:
- 提前发现错误
- 更安全地重构
- 让大代码库更可维护
另一边又在安抚 JavaScript 世界最敏感的神经:
- 不要失去 JavaScript 的手感
- 不要突然变成另一门语言
- 不要把日常写法全部推翻
也就是说,Flow 一开始根本不是走“重语言征服 JS”那条路。
它更像是在说:
我不想替换你的 JavaScript,我想更聪明地理解你的 JavaScript。
这点非常重要。
因为这让 Flow 一出来时,天然就带着一种很强的“内行感”。
它不像一个从外部来给 JavaScript 上课的人。
它更像一个从 JavaScript 世界内部长出来的、更懂日常写法的人。
这也是为什么当年很多人会觉得:
Flow 也许比 TypeScript 更“像给 JavaScript 自己准备的类型系统”。
03|Flow 真正强的地方,在于它一开始卖的不是“多写类型”,而是“少改写法,也能拿到静态分析收益”
Facebook 当年的官方文章里,把 Flow 说得很清楚:
它的目标不是逼你全面改写代码风格。
它希望通过更强的程序分析,
去适应开发者“already know and love”的那些 JavaScript idioms。
这和很多人对类型系统的刻板印象并不一样。
很多人一听静态类型,第一反应是:
- 更多注解
- 更多声明
- 更多仪式
- 更多提前规定
可 Flow 当时特别吸引人的地方,恰恰在于它试图反着来。
它更想卖的是:
我尽量多理解一点,你尽量少负担一点。
这种路线为什么会很有吸引力?
因为它特别符合 React / Facebook 那套工程文化。
那套文化长期很重视:
- 代码可维护
- 重构安全
- 工具反馈快
- 不想为了制度牺牲太多日常表达流动性
所以从产品气质上说,Flow 当时其实非常能打。
它不是靠“我是更正统的类型语言”来打。
它靠的是:
我是更像 JavaScript 内部升级的人。
04|而且别忘了,当年 React 世界本身就给了 Flow 一块天然主场
这也是第三篇必须讲清楚的一层。
因为 Flow 的优势从来不只是技术层面的。
它还有非常现实的阵地优势。
官方 React 旧文档里,专门有 Static Type Checking 页面,
明确把 Flow 和 TypeScript 一起列为大型代码库的推荐路线之一,
同时又明确写到:
Flow由 Facebook 开发Flow很常和 React 一起使用
这在当时是很有分量的。
因为 React 本身正在成为前端最重要的新中心之一。
而一门类型系统如果能和 React 这套组件世界形成天然亲和,
它就等于先拿到了一个极强的扩张跳板。
所以从历史位置上说,
Flow 并不是站在荒地里和 TypeScript 打。
它一开始站的,其实是一块很肥的地。
这也是为什么如果只用今天结果倒推,
会低估它当年的真实威胁。
05|但问题也恰恰从这里开始:Flow 很强,可它越强,越像一套需要你进入“它的项目制度”里才能充分发挥的系统
Flow 的官方使用方式,其实很能说明问题。
你要用它,通常需要:
- 装
flow-bin - 运行
flow init - 生成
.flowconfig - 在文件顶部写
// @flow - 通过 Babel 或其他工具把类型语法去掉
它当然是可用的。
而且它还有很强的后台检查体验。
可问题在于,这整套使用方式其实透露出一个很关键的事实:
Flow 更像一套项目制度。
它不是那种“你随手就已经部分进入”的系统。
它更像是在说:
“如果你愿意进入我的工作流,我会给你很强的分析能力。”
这和 TypeScript 的扩张方式有一个微妙但非常重要的区别。
TypeScript 更像:
“你先别管信不信我,你把现有 JavaScript 先带进来,我先帮你一点。”
而 Flow 更像:
“你来我的检查体系里,我给你高质量静态分析。”
这差异在小范围里不一定明显。
可一旦你把它放到整个生态 adoption 上,影响就会越来越大。
因为现代前端世界最后拼的,不只是“项目里能不能用”。
更是:
谁更容易成为默认入口。
06|Flow 后来真正吃亏的,不是它不够聪明,而是它太依赖“进入它的语境”这件事
这就是第三篇最关键的判断。
Flow 有很聪明的地方。
甚至它官方都明确把自己和 TypeScript 区分开,说它希望通过更复杂的分析去适应 JavaScript 习惯,而不是要求开发者先写很多注解。
可这种优势有一面非常漂亮,
另一面也有代价。
因为当你特别强调“我来理解你的 JavaScript”时,
你往往也更依赖:
- 项目配置
- 文件标记
- 工具链协同
- 特定生态里的最佳实践
而且随着 Flow 自身后来推进 Types-First,它也越来越明确要求模块边界上有足够注解,才能建立稳定的 file signatures。
这当然是为了性能和错误定位。
但它也说明一件事:
Flow 并不是没有制度成本,它只是把制度成本放在了另一种地方。
所以它的问题不是“不够先进”。
而是它的先进性,常常更依赖你已经处在它熟悉的工程环境里。
这就让它特别容易在强语境里很强,
却不一定最适合变成整个开放生态的默认入口。
07|而 TypeScript 这时占到的便宜,不只是类型设计,而是它更容易被脚手架、编辑器、库类型和混合项目一路收编
把前两篇和这一篇连起来看,差别就会越来越清楚。
TypeScript 一直在拼命降低 adoption 门槛:
- 现有 JavaScript 就能进来
.js和.ts可以共存- 可以先
allowJs - 可以先
checkJs - 类型是可擦除的
- 运行时现实不被改写
这意味着它特别适合被更大范围的工具链和脚手架收编成默认设置。
而一旦这件事开始发生,
胜负逻辑就会从“哪套系统更懂 JavaScript”慢慢转向另一件事:
哪套系统更容易成为大家根本不用单独决定、项目创建时就已经在那里的默认前提。
这时候 Flow 的处境就会开始变得微妙。
因为它哪怕在一些 React / Facebook 语境里依然很好用,
也不代表它更容易被整个生态打包成默认入口。
而后来的官方 React 新文档已经直接提供 Using TypeScript 页面,
Create React App 也长期提供官方 --template typescript 模板。
这类入口一旦形成,事情就会发生根本变化。
因为很多开发者根本不是通过“深入比较类型理论”做选择。
他们往往是通过:
- 文档首页怎么写
- 脚手架默认给什么
- 团队现成模板带什么
- 编辑器默认支持什么
来进入现实。
而在这件事上,TypeScript 后来越来越占上风。
08|所以 Flow 输掉的,本质上不是一场“谁更像正确类型系统”的比赛,而是一场“谁更容易成为开放生态默认空气”的比赛
把前面这些线叠一起看,第三篇最重要的判断可以压成一句:
Flow 最后输掉的,不是一场“谁更像正确类型系统”的比赛,而是一场“谁更容易成为开放生态默认空气”的比赛。
这句话里有几层意思。
第一,Flow 当年并不弱,甚至一度非常强,尤其在 Facebook / React 语境里很有主场感。
第二,它最吸引人的地方,恰恰是它看起来更懂 JavaScript 习惯,更想少改写法、多做分析。
第三,它的问题也恰恰和这套优势有关:
它更依赖进入自己的项目制度和语境,没那么容易被整个开放生态无缝收编成默认入口。
第四,TypeScript 则越来越适合被文档、脚手架、编辑器、库类型声明和混合迁移方案一起打包成“大家先用着再说”的默认前提。
所以理解 Flow 的兴衰,最不该用的写法就是:
“因为它不如 TypeScript,所以自然失败。”
更准确的写法应该是:
它在局部语境里非常强,但类型之争最后赢下来的,往往不是局部最强的那一个,而是最容易被整个生态拿去当默认入口的那一个。
09|Flow 输掉的,不只是比赛,它证明了当年并非只有一条路
TypeScript 江湖 的第三篇,最值得记住的,不是“Flow 输了”这句表层结论。
更值得记住的是:
Flow 的存在本身就说明,JavaScript 世界当年并不是只有 TypeScript 一条路。
而且它一度是一条非常像样、非常有威胁的路。
可也正因为有 Flow 这条线,
我们才更能看清楚后来真正决定胜负的东西到底是什么:
不是哪套系统看起来更酷,
也不只是哪个理论更漂亮。
而是:
谁更容易沿着 React、文档、脚手架、编辑器、项目模板和团队迁移路径,一点点长成整个生态几乎不用再讨论的默认现实。
从这个意义上说,
Flow 这条线最重要的历史价值,甚至不只是“它曾经和 TypeScript 竞争过”。
更是:
它帮整个前端世界把这场战争的真正判定标准暴露了出来。
类型之争最后拼的,从来不只是类型。
还拼:
生态。
编者注(事实核对):文中关于 Flow 于 2014-11-18 由 Facebook 正式开源发布、并被定义为“a new static type checker for JavaScript”的描述,主要依据 Facebook 官方工程博客文章 Flow, a new static type checker for JavaScript。关于 Flow 最初强调“在不失去 JavaScript 手感的前提下获得静态类型收益”“通过更复杂的程序分析适应开发者 already know and love 的 JavaScript idioms”,主要依据同一篇官方发布文,其中明确写到 “without losing the ‘feel’ of coding in JavaScript”“does not force you to change how you code”,并直接把自己与 TypeScript 这类更依赖显式注解的系统做区分。关于 Flow 的典型使用方式,如 flow init、.flowconfig、// @flow、与 Babel 配合去除类型语法,主要依据 Flow 官方 Usage、Getting Started 与 React 集成文档。关于 Flow 后来 Types-First 架构需要更多模块边界注解以建立 signatures,主要依据 Flow 官方 File Signatures (Types-First) 文档。关于 React 旧文档曾并列推荐 Flow / TypeScript,而 React 新文档已单独提供 Using TypeScript 页面、Create React App 长期提供官方 --template typescript 模板,主要依据 React 官方 Static Type Checking 旧文档、react.dev/learn/typescript 与 CRA 官方 TypeScript 模板文档。正文将这些线索综合概括为“Flow 输掉的,最终不是局部能力,而是开放生态默认入口之争”,属于对工具路线、文档入口和生态扩张方式的综合判断。
关键人物速览
- Jordan Walke:这里的重要性不在于他直接做了 Flow,而在于他所代表的 React / 静态类型 / 函数式语境,本身就是 Flow 能占到主场感的重要背景。
- Avik Chaudhuri:Flow 官方发布文作者之一。理解 Flow 最初想解决什么问题、又如何把自己和 TypeScript 区分开,绕不开他。
- Anders Hejlsberg:这里依然绕不开他,因为只有把 Flow 放在 TypeScript 对面看,才能看清后来到底是谁更容易沿着开放生态入口长成默认现实。
参考与延伸阅读
Flow, a new static type checker for JavaScript
https://engineering.fb.com/2014/11/18/web/flow-a-new-static-type-checker-for-javascript/Flow: Usage
https://flow.org/en/docs/usage/Flow with React: Getting Started
https://flow.org/en/docs/react/Flow: File Signatures (Types-First)
https://flow.org/en/docs/lang/types-first/Static Type Checking - React (legacy docs)
https://reactjs.org/docs/static-type-checking.htmlUsing TypeScript - React
https://react.dev/learn/typescriptAdding TypeScript - Create React App
https://create-react-app.dev/docs/adding-typescript/Getting Started - Create React App
https://create-react-app.dev/docs/getting-started/TypeScript: JavaScript Development at Application Scale
https://learn.microsoft.com/en-us/archive/blogs/somasegar/typescript-javascript-development-at-application-scaleTypeScript Design Goals
https://github.com/microsoft/TypeScript/wiki/TypeScript-Design-Goals