当前位置: 首页 > 知识库问答 >
问题:

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

饶德元
2023-03-14

我目前正在做一个ReactJS项目,它使用了Webpack2和TypeScript。除了一件事之外,一切都运行得很好——我找不到一种方法将我自己编写的接口移动到单独的文件中,以便它们对整个应用程序可见。

出于原型设计的目的,我最初在使用它们的文件中定义了接口,但最终我开始添加一些在多个类中需要的接口,这时所有的问题都开始了。无论我对我的< code>tsconfig.json做什么更改,也无论我把文件放在哪里,我的IDE和Webpack都抱怨找不到名称(“找不到名称‘imy interface’”)。

这是我当前的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测试过,都不行。

一种方法是将我的< code>typings目录移动到< code>src中,然后从“typings/blah”中< code >导入{IMyInterface},但我觉得这样不对,因为我不需要使用它。我希望这些接口在我的应用程序中“神奇地”可用。

以下是< code>app.d.ts文件示例:

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

我是否需要导出它们,或者可能需要声明?我希望我不必把它们包装在命名空间中?!

看到这个问题仍然令人惊讶地流行,我想更详细地解释解决方案。

首先,人们可能并且应该感到困惑的是,我在问题末尾给出的接口示例实际上没有任何< code>export关键字,尽管我几乎可以肯定在我提问时我的文件中确实有这些关键字。我相信我没有把他们包括在问题中,因为我认为不管他们在不在那里,他们都不会有任何影响。事实证明这不是真的,而< code>export关键字正是决定您是否能够“使用”这些接口,而不是显式地< code>import它们的关键。

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

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

为了给您一个示例,我们假设您希望有一个名为Dictionary的类型,您希望能够在不导入的情况下使用该类型。申报方式如下:

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

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

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

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

// 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类型的唯一方法是将其导出,然后导入消费者:

js prettyprint-override">// types.d.ts
export interface Dictionary {}
interface Foo {}
export interface Bar {}

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

const dict: Dictionary = {};

在TypeScript中使用isolatedModulesmodules标志时,还有一个需要注意的问题,重要的是,在使用Create React App-时,默认情况下会启用(并且不能禁用)该标志。ts文件必须至少导出一件事,否则您将得到“当提供'--isolatedModules'标志时,所有文件必须是模块。”错误。这意味着将Dictionary接口放在类型中。没有export关键字的ts文件将无法工作。它必须是从<code>导出的。ts</code>文件是一个声明,而不在<code>.d.ts</code>文件中导出:

// 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中是如何工作的。

共有2个答案

杜经艺
2023-03-14

这个术语是“全局”接口,而不是“神奇可用的接口”。

在一些.d.ts文件中,我扩展了全局窗口接口,如:

export {};

declare global {
    interface Window {
        // Below just informs IDE and/or TS-compiler (it's set in `.js` file).
        myGlobalVariable: any;
    }
}

使用相同的技术,我们可以创建自定义全局接口(而不是扩展现有的接口)。

import React from 'react';

declare global {
    interface MyGlobalInterface {
        myComponentField: React.Component;
    }
}

请注意,由于 .d.ts 文件不产生任何输出,我们可以导入任何模块(当然,只要该模块是通过 package.json 安装的)。

吕飞翼
2023-03-14

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

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

虽然不鼓励使用全局类型,但问题的答案是有两种<code>类型。Typescript中的ts文件。这些被称为<code>脚本</code>和<code>模块</code>。

脚本中的任何内容都是全局的。因此,如果在脚本中定义接口,它将在整个应用程序中全局可用(只要脚本通过<code>包含在编译中)///

另一种文件类型是“模块”,模块中的任何内容都将是模块私有的。如果从模块导出任何内容,则其他模块选择导入该模块时,该内容将可供其他模块使用。

是什么使. ts文件成为“脚本”或“模块”?嗯……如果您在文件中的任何位置使用导入/导出,该文件将成为“模块”。如果没有导入/导出语句,那么它就是全局脚本。

我猜您在声明中无意中使用了importexport,并将其转换为一个模块,从而将该模块中的所有接口转换为私有接口。如果您希望它们是全局的,那么您应该确保在文件中没有使用导入/导出语句。

 类似资料:
  • 我正在研究一个项目,其中上下文和会话由ThreadLocal使用ThreadPoolExecutor安全地管理(信息从线程传递到ThreadPoolExecutor内部的另一个线程)。 我们有: ThreadPoolExecutor:它实现beforeExecute和afterExecute方法行为,以确保信息从线程传递到另一个线程,并在afterExecutre方法中清除线程上下文。 Threa

  • 我用global.d.ts定义全局变量: 但是在执行main.ts的时候 报错:

  • 我正在尝试为没有它们的包装提供类型: 我在带有打字稿 2.4.2 的 webpack 中使用 ts-loader,并且在 tsconfig.json 中设置了以下类型根: 我试图模仿: < code>index.d.ts中包含以下内容: 但是错误仍然存在。我做错了什么?我应该把那些自定义的. d.ts文件放在哪里? 和任何其他类型根之间有什么区别?为什么TypeScript会区别对待它们?

  • 我的文件中有这段代码,在这里我可以简单地使用。 现在,我需要使用socket从各种文件发出事件,而不想连接此

  • 我定义了这样一个接口: 我这样定义一个变量: 然而,当我尝试设置modal的属性时,它会给我一个消息,说 使用接口来描述模态对象可以吗?如果可以,我应该如何创建它?

  • 我想定义一个带有对象和不同类型的接口,例如 在定义上,没有问题,但在调用like后 这不起作用,并出现以下错误 错误错误:未捕获(promise中):TypeError:无法设置未定义的属性“名称”TypeError:无法设置未定义的属性“名称” 有一些相似的主题,但它们并不完全相关。(如何在类型脚本接口中定义对象或者如何在类型脚本中定义对象变量的类型?) 我很感激你帮助我找到解决办法。