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


81

我目前正在研究一个使用Webpack2和TypeScript的ReactJS项目。一切工作都与一件事情完全分开-我找不到一种方法来将自己编写的接口移动到单独的文件中,以使它们对整个应用程序可见。

出于原型目的,我最初在使用它们的文件中定义了接口,但最终我开始添加多个类中需要的一些接口,这就是所有问题开始的时候。无论我对我进行了什么更改tsconfig.json,无论我将文件放在哪里,IDE和Webpack都抱怨找不到名称(“找不到名称'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"
  ]
}

如您所见,mytsconfig.json位于项目目录的根目录中,所有源位于in ./src,我将自定义.d.ts文件放在./typings并包含在中typeRoots

我使用TypeScript 2.1.6和2.2.0对其进行了测试,但均无法正常工作。

使这一切正常工作的一种方法是将typings目录移入src,然后import {IMyInterface} from 'typings/blah'对我来说感觉不合适,因为这不是我需要使用的东西。我希望这些接口在整个应用程序中都可以“神奇地”使用。

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

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

我需要export他们还是也许declare?我希望我不必将它们包装在名称空间中吗?

更新(2020年10月)

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

首先,可能并应该使人们感到困惑的是,我在问题末尾给出的接口示例实际上没有任何export关键字,即使我几乎可以肯定的是,在询问时题。我相信我没有将他们包括在内,以为他们没有任何影响,无论他们是否在那里。好吧,事实证明事实并非如此,而export关键字恰恰是造就或破坏您能够“使用”接口而不是显式接口的关键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类型的唯一方法是也将其导出,然后将其导入使用者中:

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

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

const dict: Dictionary = {};
--isolatedModules

isolatedModules在TypeScript中使用模块标志时,要记住一个额外的怪癖,重要的是,在使用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

注意
@dtabuenc在回答中提到,.d.ts不建议使用环境模块(文件),我的更正不应作为建议。这只是说明普通模块和环境模块在TypeScript中如何工作的一种尝试。

Answers:


196

强烈建议不要使用“魔术上可用的接口”或全局类型,而应将其留给传统。另外,您不应将环境声明文件(例如,d.ts文件)用于正在编写的代码。这些旨在代替外部非打字稿代码(基本上将打字稿类型填充到js代码中,以便您可以更好地将其与javascript集成)。

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

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

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

另一个文件类型是“模块”,其中的任何内容module都是该模块专用的。如果您从某个模块导出任何内容,那么如果其他模块选择导入它,则其他模块也可以使用该内容。

是什么使.ts文件成为“脚本”或“模块”?好吧....如果您使用import/export文件中的任何位置,该文​​件将成为“模块”。如果没有 import/export语句,则为全局脚本。

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


2
你是绝对正确的!事实证明,我的问题是(按照您的建议)我正在将类/接口从其他文件导入到声明文件中。一旦我删除了这些导入并只留下了清晰的interface Foo {}声明,一切就起作用了。根据全局变量-我完全明白了为什么声明要神奇地可用是一种不好的做法,并且正在考虑切换到自定义模块(实际上已经尝试过declare module MyApp { export interface Foo {} }并可以通过导入成功导入import { {Foo} from 'MyApp'),但现在魔术对我有效:)
Andris

7
该死的人。我只是浪费了一整天,不知道自己在做什么。非常感谢。如果尚未找到此答案,则需要转到官方文档。
山姆R.18年

1
我将一个项目暂停了一周,想知道为什么它不起作用。我私下学习了本来不会持续三天的东西。感谢坦克dtabuenc
撒拉杰尼斯

非常感谢。我SyntheticEvent从React导入了接口以用于我自己的类型定义,并且一切都停止了工作。我应该早点抓到的,但是我们来了。谢谢!
史蒂夫·亚当斯

@dtabuenc您可以支持不赞成使用全局类型的声明吗?查看官方文档,我无法获得那种印象。
Tomek
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.