Skip to main content

附录 D:React 源码里的 Flow 类型——读源码时怎么忽略它

读 React 源码,你可能被这类东西劝退过:

function updateReducerImpl<S, A>(hook: Hook, current: Hook, reducer: (S, A) => S): [S, Dispatch<A>] {

先放心:这些类型标注全是"给工具看的注释",不影响运行时逻辑,阅读时整段跳过即可。 但要跳得舒服,得先知道几个记号。

先记住这一句:React 内部用 Flow(不是 TypeScript)做类型标注,语法和 TS 九成相似;$FlowFixMe__DEV__: any 是三类最常见的"干扰项",认出来就不会卡壳。

为什么是 Flow 不是 TypeScript

React 在 2014 年前后定型类型方案,当时 TypeScript 对 JSX 的支持还很弱,React 选择了 Facebook 自家(现已独立)的 Flow。十几年沿用下来,源码里的类型标注全是 Flow 语法。这不影响你:npm 包自带 .d.ts,你在项目里用 TypeScript 完全正常;只是读 React 源码时,你看到的是 Flow 而不是 TS。

Flow 与 TypeScript 速查对照

九成语法一样,只需记住几个 Flow 独有/不同的记号:

FlowTypeScript含义
const x: number相同类型标注,通用写法
type Props = {...}相同类型别名
mixedunknown任意类型的"安全"版本
anyany放弃类型检查
$FlowFixMe@ts-ignore抑制类型报错
// $FlowFixMe[comment]// @ts-expect-error带原因的抑制
component(...props: any)相似组件类型
(value: any)(value as any)显式逃生舱(注意 Flow 在值后面)

前几行和你写的 TS 没区别。重点是最后两行:Flow 的逃生舱是把 : any 写在值后面,而 TS 是 as any

三个最常见的干扰项

$FlowFixMe:源码里会出现 // $FlowFixMe[missing-this-annot] 这类注释。它的意思是"这里 Flow 报错了,作者确认没问题、手动压掉"。读到直接无视——它恰恰说明"这段不是重点"。

__DEV__:这不是类型,是构建期变量。React 打包时用 Rollup 的 define__DEV__ 替换成 true(开发版)或 false(生产版)。所以:

if (__DEV__) {
// 只在开发版运行的校验、警告代码
}

阅读时,__DEV__ 包裹的整块可以直接跳过,它们和核心逻辑无关,纯粹是开发期的安全网。

③ 泛型参数updateReducerImpl<S, A> 这种 <S, A> 是泛型,等同于 TS 的 <S, A>。知道"S 是 state 类型、A 是 action 类型"这类约定,函数签名就基本能猜个大概。

阅读建议

  1. 类型标注整段忽略,只盯着运行时逻辑(函数体、变量赋值、调用链)。
  2. 遇到 $FlowFixMe: anymixed,当作"这里作者故意不深究类型",不用纠结。
  3. 想确认某个函数的参数形状时,再回来读签名,对照上面的对照表。
  4. __DEV__ 块一律跳过,那是开发期校验,不影响你理解主线。

小结

  • React 用 Flow,语法和 TS 九成相同,只多了 $FlowFixMe: any 等记号。
  • 类型标注不影响运行时,阅读时整段跳过。
  • $FlowFixMe = 作者压掉的报错,直接无视;__DEV__ = 构建期变量,包住的开发期代码跳过。
  • 你在项目里用 TS 完全不受影响,React 自带 .d.ts
  • 从此翻开任何一页源码,都不会再被类型标注绊住。