如何为 TypeScript 配置自定义全局接口(.d.ts 文件)?

How to configure custom global interfaces (.d.ts files) for TypeScript?

我目前正在开发一个使用 Webpack2 和 TypeScript 的 ReactJS 项目。除了一件事之外,一切都完美无缺 - 我无法找到一种方法将我自己编写的接口移动到单独的文件中,以便它们对整个应用程序可见。

出于原型制作的目的,我最初在使用它们的文件中定义了接口,但最终我开始添加多个 类 中需要的接口,这就是所有问题开始的时候。无论我对我的 tsconfig.json 做了什么更改,无论我把文件放在哪里,我的 IDE 和 Webpack 都会抱怨无法找到名称("Could not find name 'IMyInterface'").

这是我当前的 tsconfig.json 文件:

{
  "compilerOptions": {
    "baseUrl": "src",
    "outDir": "build/dist",
    "module": "commonjs",
    "target": "es5",
    "lib": [
      "es6",
      "dom"
    ],
    "typeRoots": [
      "./node_modules/@types",
      "./typings"
    ],
    "sourceMap": true,
    "allowJs": true,
    "jsx": "react",
    "moduleResolution": "node",
    "rootDir": "src",
    "forceConsistentCasingInFileNames": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": false,
    "strictNullChecks": true,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": true
  },
  "exclude": [
    "node_modules",
    "build",
    "scripts",
    "acceptance-tests",
    "webpack",
    "jest",
    "src/setupTests.ts"
  ],
  "types": [
    "typePatches"
  ]
}

如您所见,我的 tsconfig.json 在项目目录的根目录中,所有源代码都在 ./src 中,我将自定义 .d.ts 文件放在 ./typings 并将其包含在 typeRoots.

我使用 TypeScript 2.1.6 和 2.2.0 对其进行了测试,但均无效。

让这一切正常工作的一种方法是将我的 typings 目录移动到 src 然后 import {IMyInterface} from 'typings/blah' 但这对我来说不合适,因为这不是我想要的需要使用。我希望这些接口在我的应用程序中 'magically' 可用。

这是一个示例 app.d.ts 文件:

interface IAppStateProps {}
interface IAppDispatchProps {}
interface IAppProps extends IAppStateProps, IAppDispatchProps {}

我需要 export 他们还是 declare?我希望我不必将它们包装在命名空间中?!

更新(2020 年 10 月)

看到这个问题仍然非常受欢迎,我想更详细地解释一下解决方案。

首先,可能而且应该让人们感到困惑的是,我在问题末尾给出的界面示例实际上没有任何 export 关键字,尽管我几乎可以肯定我确实有在提出问题时,它们在我的文件中。我相信我没有将它们包括在问题中,认为它们没有任何区别,无论它们是否存在。好吧,事实证明这不是真的,export 关键字正是使您能够“使用”接口而不是必须显式 import 接口的成败因素。

因此,它在 TypeScript 中的工作方式如下:

如果您想要一个 interface/type,您可以简单地使用而无需将其导入到使用模块中,则所述接口必须驻留在 .ts 或理想情况下 .d.ts 文件中 同一文件中没有任何导入或导出。这是最重要的,因为一旦您从同一个文件中导出至少一个东西,它就会变成一个模块,并且该模块中的所有内容都必须随后由消费者导入。

举个例子,我们假设您想要一个名为 Dictionary 的类型,您希望能够在不导入的情况下使用该类型。声明方式如下:

// types.d.ts
interface Dictionary {}
interface Foo {}
interface Bar {}

要使用它,您只需执行以下操作:

// consumer.ts
const dict: Dictionary = {};

但是,如果出于某种原因导出该文件中 interfaces/types 的 any,它将不再起作用,例如:

// types.d.ts
interface Dictionary {}
interface Foo {}
export interface Bar {}

如果该文件中有导入,它也将不起作用:

// types.d.ts
import { OtherType } from 'other-library';

interface Dictionary {}
interface Foo extends OtherType {}
interface Bar {}

如果是这种情况,则能够使用 Dictionary 类型的唯一方法是也将其导出,然后将其导入到消费者中:

// types.d.ts
export interface Dictionary {}
interface Foo {}
export interface Bar {}

// consumer.ts
import { Dictionary } from './types';

const dict: Dictionary = {};
--isolatedModules

在 TypeScript 中使用 isolatedModules 模块标志时需要记住一个额外的怪癖,重要的是,在使用 Create React App 时默认启用(并且不能禁用)- .ts 文件必须至少导出一件事,否则你将得到 “当提供 '--isolatedModules' 标志时,所有文件必须是模块。” 错误。这意味着将 Dictionary 接口放在 types.ts 文件中而不使用 export 关键字是行不通的。它必须是从 .ts 文件中导出或者是在 .d.ts 文件中没有导出的声明:

// types.d.ts
interface Dictionary {}        // works
export interface Dictionary {} // works

// types.ts
interface Dictionary {}        // doesn't work with --isolatedModules enabled
export interface Dictionary {} // works

N.B.
正如@dtabuenc 在他的回答中提到的,环境模块(.d.ts 文件)是不受欢迎的,我的更正不应被视为建议。这只是试图解释普通模块和环境模块在 TypeScript 中的工作方式。

"Magically available interfaces" 或全局类型是非常不鼓励的,应该主要留给遗产。此外,您不应为正在编写的代码使用环境声明文件(例如 d.ts 文件)。这些是为了stand-in外部non-typescript代码的位置(本质上是将打字稿类型填充到js代码中,以便您可以更好地将其与javascript集成)。

对于您编写的代码,您应该使用纯 .ts 文件来定义您的接口和类型。

虽然不鼓励使用全局类型,但您的问题的答案是 Typescript 中有两种类型的 .ts 文件。这些称为 scriptsmodules

script 中的任何内容都是全局的。因此,如果您在脚本中定义接口,它将在整个应用程序中全局可用(只要脚本通过 ///<reference path=""> 标记或通过 files:[]includes:[] 包含在编译中或tsconfig.json.

中的默认 **/*.ts

另一种文件类型是 'module',module 中的任何内容都是模块私有的。如果您从一个模块中导出任何内容,如果其他模块选择导入它,那么其他模块也可以使用它。

什么使 .ts 文件成为 "script" 或 "module"?好吧……如果您在文件中的任何地方使用 import/export,该文件就会变成 "module"。如果没有 import/export 语句那么它是一个全局脚本。

我的猜测是您在声明中无意中使用了 importexport 并将其放入模块中,这将您在该模块中的所有接口变为私有。如果您希望它们是全局的,那么您将确保您没有在文件中使用 import/export 语句。