【问题标题】:How to configure custom global interfaces (.d.ts files) for TypeScript?如何为 TypeScript 配置自定义全局接口(.d.ts 文件)?
【发布时间】:2017-07-03 04:55:42
【问题描述】:

我目前正在开发一个使用 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"
  ]
}

如您所见,我的tsconfig.json在项目目录的根目录下,所有源代码都在./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

在 TypeScript 中使用 isolatedModules 模块标志时要记住一个额外的怪癖,重要的是,在使用 Create React App 时默认启用(并且不能禁用) - .ts 文件必须导出至少有一件事,否则你会得到 “当提供 '--isolatedModules' 标志时,所有文件必须是模块。” 错误。这意味着将Dictionary 接口放在没有export 关键字的types.ts 文件中是行不通的。它必须是来自 .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 中是如何工作的。

【问题讨论】:

    标签: typescript typescript-typings typescript2.0 typescript1.8


    【解决方案1】:

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

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

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

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

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

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

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

    【讨论】:

    • 你说的完全正确!原来我的问题是(正如你所建议的)我正在将其他文件中的类/接口导入到我的声明文件中。一旦我删除了这些导入并且只留下了干净的interface Foo {} 声明,一切正常。根据 globals - 我完全明白为什么让声明神奇地可用是一种不好的做法,并且正在考虑切换到自定义模块(实际上尝试了 declare module MyApp { export interface Foo {} } 并且可以通过执行 import { {Foo} from 'MyApp' 成功导入它)但现在魔法对我有用:)
    • 天哪。我只是浪费了一整天不知道自己在做什么。非常感谢。如果还没有,这个答案需要去官方文档。
    • 我暂停了一个项目一周,想知道为什么它不起作用。我私下学习了原本不会持续三天的东西。谢谢dtabuenc
    • @dtabuenc 你能支持你关于全局类型被弃用的说法吗?查看官方文档我无法获得那种印象。
    • “神奇的可用接口”是为基于 JS 的 DSL 提供基本 IDE 支持的两种方式之一,另一种方式是如果不是数千次更难,也有数百次 : 为所有 IDE 编写插件。
    猜你喜欢
    • 2015-10-11
    • 2016-10-18
    • 2018-01-06
    • 2017-03-02
    • 2017-05-08
    • 2016-02-15
    • 2022-11-05
    • 2018-09-16
    • 2016-01-02
    相关资源
    最近更新 更多