创建库定义
在花时间编写自己的 libdef 之前,我们建议你查看一下是否已经有你正在处理的第三方代码的 libdef。flow-typed
是用于在 Flow 社区内共享公共 libdef 的 工具和存储库 ——因此,这是一个很好的方法,可以消除你的项目可能需要的大部分公共 libdef。
¥Before spending the time to write your own libdef, we recommend that you look to
see if there is already a libdef for the third-party code that you're addressing.
flow-typed
is a tool and repository
for sharing common libdefs within the Flow community -- so it's a good way to
knock out a good chunk of any public libdefs you might need for your project.
然而,有时没有预先存在的 libdef,或者你有不公开的第三方代码和/或你实际上只需要自己编写一个 libdef。为此,你将首先为要编写的每个 libdef 创建一个 .js
文件,并将它们放入项目根目录的 /flow-typed
目录中。在这些 libdef 文件中,你将使用一组特殊的 Flow 语法(如下所述)来描述相关第三方代码的接口。
¥However sometimes there isn't a pre-existing libdef or you have third-party
code that isn't public and/or you really just need to write a libdef yourself.
To do this you'll start by creating a .js
file for each libdef you're going to
write and put them in the /flow-typed
directory at the root of your project.
In these libdef file(s) you'll use a special set of Flow syntax (explained
below) to describe the interfaces of the relevant third-party code.
声明全局函数
¥Declaring A Global Function
要声明可在整个项目中访问的全局函数,请在 libdef 文件中使用 declare function
语法:
¥To declare a global function that should be accessible throughout your project,
use the declare function
syntax in a libdef file:
flow-type/myLibDef.js
1declare function foo(a: number): string;
这告诉 Flow 项目中的任何代码都可以引用 foo
全局函数,并且该函数采用一个参数(number
)并返回 string
。
¥This tells Flow that any code within the project can reference the
foo
global function, and that the function takes one argument (a number
) and
it returns a string
.
声明全局类
¥Declaring A Global Class
要声明一个可以在整个项目中访问的全局类,请在 libdef 文件中使用 declare class
语法:
¥To declare a global class that should be accessible throughout your project,
use the declare class
syntax in a libdef file:
flow-type/myLibDef.js
1declare class URL {2 constructor(urlStr: string): URL;3 toString(): string;4
5 static compare(url1: URL, url2: URL): boolean;6}
这告诉 Flow 项目中的任何代码都可以引用 URL
全局类。请注意,此类定义没有任何实现细节 - 它专门定义了类的接口。
¥This tells Flow that any code within the project can reference the URL
global
class. Note that this class definition does not have any implementation details
-- it exclusively defines the interface of the class.
声明全局变量
¥Declaring A Global Variable
要声明可在整个项目中访问的全局变量,请在 libdef 文件中使用 declare var
、declare let
或 declare const
语法:
¥To declare a global variable that should be accessible throughout your project,
use the declare var
, declare let
, or declare const
syntax in a libdef file:
flow-type/myLibDef.js
1declare const PI: number;
这告诉 Flow 项目中的任何代码都可以引用 PI
全局变量 - 在本例中是 number
。
¥This tells Flow that any code within the project can reference the PI
global
variable -- which, in this case, is a number
.
声明全局类型
¥Declaring A Global Type
要声明可在整个项目中访问的全局类型,请在 libdef 文件中使用 declare type
语法:
¥To declare a global type that should be accessible throughout your project,
use the declare type
syntax in a libdef file:
flow-type/myLibDef.js
1declare type UserID = number;
这告诉 Flow 项目中的任何代码都可以引用 UserID
全局类型 - 在本例中,它只是 number
的别名。
¥This tells Flow that any code within the project can reference the UserID
global type -- which, in this case, is just an alias for number
.
声明全局命名空间
¥Declaring A Global Namespace
命名空间定义值和类型的集合:
¥A namespace defines a collection of values and types:
1declare namespace Foo {2 declare const bar: string;3 type Baz = number;4}5
6Foo.bar as Foo.Baz; // error
要声明应在整个项目中可访问的全局命名空间,请在 libdef 文件中使用 declare namespace
语法:
¥To declare a global namespace that should be accessible throughout your project,
use the declare namespace
syntax in a libdef file:
flow-type/myLibDef.js
1declare namespace Foo {2 declare const bar: string;3 type Baz = number;4}
这告诉 Flow,项目中的任何代码都可以引用 Foo
全局命名空间。
¥This tells Flow that any code within the project can reference the Foo
global
namespace.
如果声明的命名空间仅包含类型声明,则命名空间本身只能在类型上下文中使用。
¥If a declared namespace only contains type declarations, then the namespace itself can only be used in a type context.
flow-typed/myTypeOnlyNamespace.js
1declare namespace TypeOnlyFoo {2 type Baz = number;3}4
5TypeOnlyFoo; // error6type T = TypeOnlyFoo.Baz; // ok
声明模块
¥Declaring A Module
通常,第三方代码是按照模块而不是全局变量来组织的。Flow 提供了两种不同的模块声明方式。
¥Often, third-party code is organized in terms of modules rather than globals. Flow offers two different ways to declare a module.
在 @flowtyped
目录中声明模块
¥Declaring a module in the @flowtyped
directory
{#toc-declaring-a-module-in-at-flowtyped}
自 v0.251.0 以来,Flow 增加了对在项目根目录的 @flowtyped
目录中轻松声明第三方模块的支持。在查看 node_modules
中的模块说明符 foo/bar/baz
之前,Flow 将查看 @flowtyped/foo/bar/baz.js.flow
和 @flowtyped/foo/bar/baz/index.js.flow
。
¥Since v0.251.0, Flow has added support for easily declaring third-party modules in the
@flowtyped
directory at the root of the project. Before looking into node_modules
for
the module specifier foo/bar/baz
, Flow will look into @flowtyped/foo/bar/baz.js.flow
and
@flowtyped/foo/bar/baz/index.js.flow
.
例如,如果你想声明 react
的类型,你可以执行以下操作:
¥For example, if you want to declare the types for react
, you can do:
export type SetStateFunction<S> = ((S => S) | S) => void;
declare export function useState<S>(initial: S): [S, SetStateFunction<S>];
// Other stuff...
这将允许你从 react
导入这些函数和类型:
¥which will allow you to import these functions and types from react
:
import * as React from 'react';
function MyComponent({onSelect}: {onSelect: React.SetStateFunction<string>}) {
const [a, setA] = React.useState(new Set<string>());
return <div />;
}
// Other stuff...
如果你想声明 @my-company/library-a
等作用域包的类型,你可以执行以下操作
¥If you want to declare the types for a scoped package like @my-company/library-a
, you can do
declare export const foo: string;
declare export const bar: number;
如果你想声明 react-native/internals/foo
等包中深度嵌套模块的类型,你可以执行以下操作:
¥If you want to declare the types for a deeply nested module in a package like
react-native/internals/foo
, you can do:
declare export const SECRET_INTERNALS_Foo: {...};
这种方法比 below 中描述的方法更可取,因为编辑这些文件不会触发 Flow 服务器的重新启动。
¥This approach is preferrable to the approach described below, because editing these files will not trigger a restart of Flow server.
在全局命名空间中声明模块
¥Declaring a module in the global namespace
你还可以使用 declare module
语法声明模块:
¥You can also declare modules using the declare module
syntax:
declare module "some-third-party-library" {
// This is where we'll list the module's exported interface(s)
}
declare module
后面的引号中指定的名称可以是任何字符串,但它应该与你用于将第三方模块 require
或 import
插入项目的字符串相对应。要定义通过相对 require
/import
路径访问的模块,请参阅 .flow
文件 上的文档
¥The name specified in quotes after declare module
can be any string, but it
should correspond to the same string you'd use to require
or import
the
third-party module into your project. For defining modules that are accessed via
a relative require
/import
path, please see the docs on the .flow
files
在 declare module
块的主体中,你可以指定该模块的导出集。然而,在我们开始讨论导出之前,我们必须先讨论 Flow 支持的两种模块:CommonJS 和 ES 模块。
¥Within the body of a declare module
block, you can specify the set of exports
for that module. However, before we start talking about exports we have to talk
about the two kinds of modules that Flow supports: CommonJS and ES modules.
Flow 可以处理 CommonJS 和 ES 模块,但是两者之间存在一些相关差异,在使用 declare module
时需要考虑。
¥Flow can handle both CommonJS and ES modules, but there are some relevant
differences between the two that need to be considered when using
declare module
.
声明 ES 模块
¥Declaring An ES Module
ES 模块 有两种导出:命名导出和默认导出。Flow 支持在 declare module
主体内声明其中一种或两种导出的能力,如下所示:
¥ES modules
have two kinds of exports: A named export and a default export. Flow supports the ability
to declare either or both of these kinds of exports within a declare module
body as follows:
命名导出
¥Named Exports
flow-type/some-es-module.js
declare module "some-es-module" {
// Declares a named "concatPath" export
declare export function concatPath(dirA: string, dirB: string): string;
}
请注意,你还可以在 declare module
的主体内声明其他内容,这些内容的范围将限定在 declare module
的主体内 - 但它们不会从模块中导出:
¥Note that you can also declare other things inside the body of the
declare module
, and those things will be scoped to the body of the
declare module
-- but they will not be exported from the module:
flow-type/some-es-module.js
declare module "some-es-module" {
// Defines the type of a Path class within this `declare module` body, but
// does not export it. It can only be referenced by other things inside the
// body of this `declare module`
declare class Path {
toString(): string;
}
// Declares a named "concatPath" export which returns an instance of the
// `Path` class (defined above)
declare export function concatPath(dirA: string, dirB: string): Path;
}
默认导出
¥Default Exports
flow-type/some-es-module.js
declare module "some-es-module" {
declare class URL {
constructor(urlStr: string): URL;
toString(): string;
static compare(url1: URL, url2: URL): boolean;
}
// Declares a default export whose type is `typeof URL`
declare export default typeof URL;
}
还可以在同一个 declare module
主体中声明命名导出和默认导出。
¥It is also possible to declare both named and default exports in the
same declare module
body.
声明 CommonJS 模块
¥Declaring A CommonJS Module
CommonJS 模块有一个导出的值(module.exports
值)。要描述 declare module
主体中此单个值的类型,你将使用 declare module.exports
语法:
¥CommonJS modules have a single value that is exported (the module.exports
value). To describe the type of this single value within a declare module
body, you'll use the declare module.exports
syntax:
flow-type/some-commonjs-module.js
declare module "some-commonjs-module" {
// The export of this module is an object with a "concatPath" method
declare module.exports: {
concatPath(dirA: string, dirB: string): string;
};
}
请注意,你还可以在 declare module
的主体内声明其他内容,这些内容的作用域将限于 declare module
的主体,但它们不会从模块中导出:
¥Note that you can also declare other things inside the body of the
declare module
, and those things will be scoped to the body of the
declare module
, but they will not be exported from the module:
flow-type/some-commonjs-module.js
declare module "some-commonjs-module" {
// Defines the type of a Path class within this `declare module` body, but
// does not export it. It can only be referenced by other things inside the
// body of this `declare module`
declare class Path {
toString(): string;
}
// The "concatPath" function now returns an instance of the `Path` class
// (defined above).
declare module.exports: {
concatPath(dirA: string, dirB: string): Path
};
}
注意:因为给定的模块不能同时是 ES 模块和 CommonJS 模块,所以在同一个 declare module
主体中混合 declare export [...]
和 declare module.exports: ...
是错误的。
¥NOTE: Because a given module cannot be both an ES module and a CommonJS module,
it is an error to mix declare export [...]
with
declare module.exports: ...
in the same declare module
body.