如何为 TypeScript 配置自定义全局接口(.d.ts 文件)? [英] How to configure custom global interfaces (.d.ts files) for TypeScript?

查看:401
本文介绍了如何为 TypeScript 配置自定义全局接口(.d.ts 文件)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm currently working on a ReactJS project which uses Webpack2 and TypeScript. Everything works perfectly apart from one thing - I can't a find a way to move interfaces that I've written myself into separate files so that they are visible to the whole application.

出于原型设计的目的,我最初在使用它们的文件中定义了接口,但最终我开始添加一些在多个类中需要的接口,这就是所有问题的开始.无论我对 tsconfig.json 做了什么更改,也无论我将文件放在哪里,我的 IDE 和 Webpack 都抱怨找不到名称(找不到名称"IMyInterface'').

For prototyping purposes I initially had interfaces defined in files that use them but eventually I started adding some that were needed in multiple classes and that's when all the problems started. No matter what changes I make to my tsconfig.json and no matter where I put the files my IDE and Webpack both complain about not being able to find names ("Could not find name 'IMyInterface'").

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

Here's my current tsconfig.json file:

{
  "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中,我放置了我自定义的./typings 中的 .d.ts 文件并将其包含在 typeRoots 中.

As you can see, my tsconfig.json is in the root of the project directory, all source is in ./src, I placed my custom .d.ts files in ./typings and included it in typeRoots.

我使用 TypeScript 2.1.6 和 2.2.0 对其进行了测试,但都不起作用.

I tested it with TypeScript 2.1.6 and 2.2.0 and neither works.

让这一切正常工作的一种方法是将我的 typings 目录移动到 src 中,然后 import {IMyInterface} from 'typings/blah' 但这对我来说感觉不对,因为它不是我需要使用的东西.我希望这些接口在我的应用程序中神奇地"可用.

One way of getting it all to work is to move my typings directory into src and then import {IMyInterface} from 'typings/blah' but that doesn't feel right to me as it's not something I need to use. I want those interfaces to just be 'magically' available throughout my application.

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

Here's a sample app.d.ts file:

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

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

Do I need to export them or maybe declare? I hope I don't have to wrap them in a namespace?!

看到这个问题仍然出奇的受欢迎,我想更详细地解释解决方案.

Seeing how this question is still surprisingly popular I wanted to explain the solution in more detail.

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

Firstly, what could and should be confusing to people is that the interface example I gave at the end of my question actually doesn't have any export keywords even though I'm almost certain I did have them in my files at the time of asking the question. I believe I didn't include them in the question thinking they didn't make any difference, whether they were there or not. Well, it turns out that it's not true and the export keyword is exactly what makes or breaks you being able to just "use" the interfaces versus having to explicitly import them.

所以,它在 TypeScript 中的工作方式如下:

So, the way it works in TypeScript is as follows:

如果你想要一个接口/类型,你可以简单地使用它而不必在消费模块中导入它,所述接口必须驻留在 .ts 或理想情况下 .d.ts 文件 在同一文件中不存在任何导入或导出.这一点至关重要,因为一旦您从同一文件中导出至少一项内容,它就会成为一个模块,并且该模块中的所有内容都必须随后由消费者导入.

If you want an interface/type that you can simply use without having to import it in the consuming module said interface must reside in a .ts or ideally a .d.ts file without any imports or exports being present in the same file. This is of utmost importance because as soon as you are exporting at least one thing from the same file it becomes a module and everything that is in that module must be subsequently imported by consumers.

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

To give you an example let's assume you want to have a type called Dictionary that you want to be able to use without importing. The way to declare it would be as follows:

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

要使用它,您只需:

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

但是,如果由于某种原因任何该文件中的接口/类型被导出,它将不再起作用,例如:

However, it will no longer work if for some reason any of the interfaces/types in that file are exported, e.g.:

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

如果该文件中有导入,它也不会工作:

It will also not work if there are imports in that file:

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

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

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

If that is the case the only way to be able to use the Dictionary type would be to also export it and then import it in the consumer:

// 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 接口放在没有 export 关键字的 types.ts 文件中是行不通的.它必须是从 .ts 文件导出的,或者是在 .d.ts 文件中没有导出的声明:

--isolatedModules

There is an additional quirk to keep in mind when using the isolatedModules modules flag in TypeScript, which, importantly, is enabled by default (and cannot be disabled) when using Create React App - .ts files MUST export at least one thing as otherwise you will be getting the "All files must be modules when the '--isolatedModules' flag is provided." error. That means that putting the Dictionary interface in a types.ts files without the export keyword won't work. It must either be an export from a .ts file of be a declaration without the export in a .d.ts file:

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

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

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

N.B.
As @dtabuenc mentions in his answer ambient modules (.d.ts files) are discoraged and my correction shouldn't be taken as advice. It's just an attempt at explaining how normal modules and ambient modules work in TypeScript.

推荐答案

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

"Magically available interfaces" or global types is highly discouraged and should mostly be left to legacy. Also, you should not be using ambient declaration files (e.g. d.ts files) for code that you are writing. These are meant to stand-in the place of external non-typescript code (essentially filling in the typescript types into js code so that you can better integrate it with javascript).

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

For code you write you should be using plain .ts files to define your interfaces and types.

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

While global types are discouraged, the answer to your issue is that there are two types of .ts files in Typescript. These are called scripts and modules.

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

Anything in a script will be global. So if you define your interfaces in a script it will be available globally throughout your application (as long as the script is included in the compilation through either ///<reference path=""> tags or through files:[] or includes:[] or the default **/*.ts in your tsconfig.json.

另一种文件类型是模块",module 中的任何内容都将是该模块私有的.如果你从一个模块中导出任何东西,如果其他模块选择导入它,它就可以被其他模块使用.

The other file type is 'module', and anything in a module will be private to the module. If you export anything from a module it will be available to other modules if those other modules chose to import it.

什么使 .ts 文件成为脚本"或模块"?嗯.... 如果您在文件中的任何位置使用 import/export,该文件将成为模块".如果没有 import/export 语句,则它是一个全局脚本.

What makes a .ts file a "script" or a "module"? Well.... if you use import/export anywhere in the file, that file becomes a "module". If there are no import/export statements then it is a global script.

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

My guess is you have inadvertently used import or export in your declarations and made it into a module, which turned all your interfaces to private within that module. If you want them to be global then you would make sure you are not using import/export statements within your file.

这篇关于如何为 TypeScript 配置自定义全局接口(.d.ts 文件)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆