Skip to main content

创建库定义

在花时间编写自己的 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 vardeclare letdeclare 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

0.251.0

{#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:

@flowtyped/react.js.flow
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:

foo/bar/baz/my-product-code.jsx
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

@flowtyped/@my-company/library-a.js.flow
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:

@flowtyped/react-native/internals/foo.js.flow
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 后面的引号中指定的名称可以是任何字符串,但它应该与你用于将第三方模块 requireimport 插入项目的字符串相对应。要定义通过相对 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.