声明文件
什么是声明文件?
让我们看看一种更通用、有时更方便的为模块声明类型的方式:.flow
文件。
根据是否存在实现文件,有两种可能的用例。
在第一种情况下,模块的导出类型在声明文件 <FILENAME>.flow
中声明,该文件位于与相应的实现文件 <FILENAME>
相同的目录中。声明文件完全覆盖了同位置的实现。换句话说,Flow 将完全忽略 <FILENAME>
,而只读取 <FILENAME>.flow
。
在第二种情况下,实现文件完全缺失。<FILENAME>.flow
被视为名为 <FILENAME>
的文件。
请注意,.flow
扩展名既适用于 .js
文件,也适用于 .json
文件。相应的声明文件分别具有扩展名 .js.flow
和 .json.flow
。
现在让我们看一个上面记录的第一种情况的例子。假设我们在一个名为 src/LookBeforeYouLeap.js
的文件中拥有以下代码
import { isLeapYear } from "./Misc";
if (isLeapYear("2020")) console.log("Yay!");
并假设 src/Misc.js
拥有一个与 isLeapYear
不兼容的实现
1export function isLeapYear(year: number): boolean {2 return year % 4 == 0; // yeah, this is approximate3}
如果我们现在创建一个声明文件 src/Misc.js.flow
,其中的声明将被用来代替 src/Misc.js
中的代码。假设我们在 src/Misc.js.flow
中拥有以下声明。
注意:声明文件中的声明语法与我们在 创建库定义部分 中看到的相同。
1declare export function isLeapYear(year: string): boolean;
你认为会发生什么?
没错,src/LookBeforeYouLeap.js
中的 isLeapYear
调用将通过类型检查,因为 year
参数在声明文件中期望一个 string
。
如本例所示,声明文件必须谨慎编写:确保它们正确是程序员的责任,否则它们可能会隐藏类型错误。
在常规代码中内联声明
有时在实现文件的源代码中内联声明非常有用。
在以下示例中,假设你想完成编写函数 fooList
,而不想先费心模拟其依赖项:一个接受 number
并返回 string
的函数 foo
,以及一个拥有 map
方法的类 List
。你可以通过包含 List
和 foo
的声明来做到这一点
1declare class List<T> {2 map<U>(f: (x: T) => U): List<U>;3}4declare function foo(n: number): string;5
6function fooList(ns: List<number>): List<string> {7 return ns.map(foo);8}
只是别忘了用正确的实现替换声明。