如何将其他类型导入我的 TypeScript 自定义声明文件?

How to import other types into my TypeScript custom declarations file?

我有这个 TypeScript + React + Webpack + Jest + Enzyme 可以正常工作。

我觉得有必要在我的测试规范中提供一些可访问的全局函数。我可以通过将 Jest 配置中的 setupTestFrameworkScriptFile 选项指向 .js 文件来做到这一点,我有这样的东西:

const Enzyme = require("enzyme");
const React = require("react");

const getMuiTheme = require("material-ui/styles/getMuiTheme").default;

global.mountWithContext = (node) => {
    return Enzyme.mount(node, {
        context: {
            muiTheme: getMuiTheme()
        },
        childContextTypes: {
            muiTheme: React.PropTypes.object
        }
    });
};

在我的规范文件上,我可以调用 mountWithContext(),当我 运行 测试时它会工作得很好。但是我的编辑器不识别这个函数。

为了解决这个问题,我创建了一个 /typings/declarations.d.ts 文件:

declare function mountWithContext(node: any): any;

现在我的规范文件可以识别 mountWithContext 函数,但类型是 "wrong"。正确的定义应该是这样的:

declare function mountWithContext(node: React.ReactElement<any>): Enzyme.ReactWrapper<P, S>;

React.ReactElement 被正确识别,因为 @types/react/index.d.ts 文件包括:

export = React;
export as namespace React;

酵素并非如此。没有导出全局 Enzyme。看着 @types/enzyme/index.d.ts 有这样的东西:

export interface ReactWrapper<P, S> extends CommonWrapper<P, S> {
    (...)
}

但即使我这样做:

declare function mountWithContext(node: React.ReactElement<any>): ReactWrapper<P, S>;

没用。 ReactWrapper 仍然无法识别。

我试图在这个 declarations.d.ts 文件中导入,这将允许我正确使用类型,但我的函数声明将不再在我的规范文件中被识别。我还尝试添加一个像这样的三斜线指令:

/// <reference path="../node_modules/@types/enzyme/index.d.ts" />

文件已被识别,但 ReactWrapper 类型仍未识别。

所以...我怎样才能为我的自定义 mountWithContext 全局函数创建一个 TypeScript 函数声明,其中包含所有正确的类型,如下所示:

declare function mountWithContext(node: React.ReactElement<any>): ReactWrapper<P, S>;

函数签名错误,必须先声明泛型,例如:

function foo(T: bar): T // wrong
function foo<T>(T: bar): T // right
function foo<T extends K, K>(T: bar): K // right

在你的情况下,这样的事情应该有效:

declare function mountWithContext<P, S>(node: React.ReactElement<any>): ReactWrapper<P, S>;

如果您碰巧知道 PS 是什么,您可以更具体一些,例如使用 <P extends React.Component, K>

如果我们想声明 global.mountWithContext 函数存在并且具有指定的类型,我们可以将以下内容添加到项目中的空声明文件中。

import React from 'react';
import enzyme from 'enzyme';

declare global {
  namespace NodeJS {
    interface Global {
      mountWithContext<P, S>(node: React.ReactElement<any>): enzyme.ReactWrapper<P, S>;
    }
  }
}

详情

下面是上面每个构造的分解:

  1. declare global 块:这个构造的目的是修改,如果你愿意的话,扩大全局范围,通过引入新声明或修改现有声明,从 within 非全局上下文,例如模块。我们的声明文件是一个模块,因为它包含一个从 Enzyme 导入 ReactWrapper 的 top-level import 子句。 这样的块称为 "Global Augmentation",也可能出现在实现文件中。

  2. interface Global 包裹在 namespace NodeJS 结构中:在我们的测试中,我们正在访问 mountWithContext 函数作为全局变量的成员,顺便说一下 global,这是Node环境提供的。此变量已由 Node 的类型声明声明在与其他环境全局变量(例如 process)相同的级别。但是,我们需要通过添加新成员来扩充其 type。这个全局global类型是官方已经在namespaceNodeJS中声明的interfaceGlobal节点的声明。我们通过声明 interface Global 的新成员来实现这一点。我们的扩充需要包含在包含 Globalnamespace 中,否则它将被视为单独的 interface。粗略地说,词法范围适用。 (回想一下,TypeScript 接口可以在同一范围内多次声明,并且这些声明将被合并)

备注

我们把这个声明放在哪里? 任何会导致 TypeScript 获取它并将其包含在我们的编译上下文中的地方。 按照惯例,我们将把它放在项目根目录中名为 globals.d.ts 的文件中。 (从技术上讲,它可以命名为其他名称并进入较低的目录)。

请务必注意,此声明文件是我们应用程序代码的一部分,应检入源代码管理。 它必须 not 与第三方声明一起放在 typings 等目录中node_modules/@types,或jspm_packages/npm/@types.

同样重要的是要注意,此增强仅影响 TypeScript 文件(.ts.tsx.d.ts)。如果我们宁愿声明此函数由 TypeScript 语言服务获取,只是为了在 .js.jsx, 文件中工作时提供智能感知,则此方法将不起作用。