我目前正在开发一个使用 Webpack2 和 TypeScript 的 ReactJS 项目。除了一件事之外,一切都完美无缺 - 我找不到将自己编写的接口移动到单独文件中的方法,以便它们对整个应用程序可见。
出于原型设计的目的,我最初在使用它们的文件中定义了接口,但最终我开始添加一些在多个类中需要的接口,这就是所有问题开始的时候。无论我对我的文件进行什么更改tsconfig.json,无论我将文件放在哪里,我的 IDE 和 Webpack 都抱怨无法找到名称( “找不到名称 ‘IMyInterface’” )。
tsconfig.json
这是我当前的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.
./src
.d.ts
./typings
typeRoots
我用 TypeScript 2.1.6 和 2.2.0 对其进行了测试,但都不起作用。
让这一切正常工作的一种方法是将我的typings目录移动到src然后import {IMyInterface} from 'typings/blah',但这对我来说感觉不对,因为它不是我需要使用的东西。我希望这些接口在我的应用程序中“神奇地”可用。
typings
src
import {IMyInterface} from 'typings/blah'
这是一个示例app.d.ts文件:
app.d.ts
interface IAppStateProps {} interface IAppDispatchProps {} interface IAppProps extends IAppStateProps, IAppDispatchProps {}
我需要export他们还是也许declare?我希望我不必将它们包装在名称空间中?!
export
declare
看到这个问题仍然非常受欢迎,我想更详细地解释解决方案。
首先,可能而且应该让人们感到困惑的是,我在问题末尾给出的界面示例实际上没有任何export关键字,即使我几乎可以肯定在询问时我的文件中确实有它们问题。我相信我没有将它们包括在问题中,认为它们没有任何区别,无论它们是否存在。好吧,事实证明这不是真的,而export关键字正是使您能够“使用”接口而不是必须显式使用接口的原因import。
import
因此,它在 TypeScript 中的工作方式如下:
如果您想要一个可以简单地使用而无需在消费模块中导入它的接口/类型,则该接口必须驻留在一个文件中,.ts或者理想情况下是一个.d.ts文件 ,而同一文件中不存在任何导入或导出 。这是最重要的,因为一旦您从同一个文件中导出至少一个东西,它就会变成一个模块,并且该模块中的所有内容都必须随后由消费者导入。
.ts
举个例子,让我们假设您想要一个名为的类型Dictionary,您希望能够在不导入的情况下使用它。声明方式如下:
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中使用 modules 标志时要记住一个额外的怪癖isolatedModules,重要的是,在使用 Create React App 时默认启用(并且不能禁用) -.ts文件必须至少导出一件事,否则你会得到“当提供 ‘–isolatedModules’ 标志时, 所有文件必须是模块。” 错误。这意味着将Dictionary接口放在types.ts没有 export 关键字的文件中是行不通的。它必须是从.ts文件中导出的声明,而在.d.ts文件中没有导出:
isolatedModules
types.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 中工作的尝试。
“神奇的可用接口”或全局类型是非常不鼓励的,应该主要留给遗留。此外,您不应该d.ts对正在编写的代码使用环境声明文件(例如文件)。这些是为了代替外部非打字稿代码(本质上是将打字稿类型填充到 js 代码中,以便您可以更好地将其与 javascript 集成)。
d.ts
对于您编写的代码,您应该使用普通.ts文件来定义您的接口和类型。
虽然不鼓励使用全局类型,但您的问题的答案是 Typescript 中有两种类型的.ts文件。这些被称为scripts和modules。
scripts
modules
a 中的任何内容script都是全局的。因此,如果您在脚本中定义接口,它将在整个应用程序中全局可用(只要脚本通过///<reference path="">标签或通过files:[]orincludes:[]或**/*.ts您的tsconfig.json.
script
///<reference path="">
files:[]
includes:[]
**/*.ts
另一种文件类型是“模块”,a 中的任何内容都是module模块私有的。如果您从一个模块中导出任何内容,那么如果其他模块选择导入它,它将可供其他模块使用。
module
是什么让.ts文件成为“脚本”或“模块”?好吧....如果您import/export在文件中的任何位置使用,该文件将成为“模块”。如果没有 import/export语句,那么它是一个全局脚本。
import/export
我的猜测是您无意中在声明中使用了importorexport并将其制成了一个模块,这将您的所有接口都变成了该模块中的私有接口。如果您希望它们是全局的,那么您将确保您没有在文件中使用导入/导出语句。