如何为 TypeScript 配置自定义全局接口(.d.ts 文件)?
How to configure custom global interfaces (.d.ts files) for TypeScript?
我目前正在开发一个使用 Webpack2 和 TypeScript 的 ReactJS 项目。除了一件事之外,一切都完美无缺 - 我无法找到一种方法将我自己编写的接口移动到单独的文件中,以便它们对整个应用程序可见。
出于原型制作的目的,我最初在使用它们的文件中定义了接口,但最终我开始添加多个 类 中需要的接口,这就是所有问题开始的时候。无论我对我的 tsconfig.json
做了什么更改,无论我把文件放在哪里,我的 IDE 和 Webpack 都会抱怨无法找到名称("Could not find name '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'
但这对我来说不合适,因为这不是我想要的需要使用。我希望这些接口在我的应用程序中 'magically' 可用。
这是一个示例 app.d.ts
文件:
interface IAppStateProps {}
interface IAppDispatchProps {}
interface IAppProps extends IAppStateProps, IAppDispatchProps {}
我需要 export
他们还是 declare
?我希望我不必将它们包装在命名空间中?!
更新(2020 年 10 月)
看到这个问题仍然非常受欢迎,我想更详细地解释一下解决方案。
首先,可能而且应该让人们感到困惑的是,我在问题末尾给出的界面示例实际上没有任何 export
关键字,尽管我几乎可以肯定我确实有在提出问题时,它们在我的文件中。我相信我没有将它们包括在问题中,认为它们没有任何区别,无论它们是否存在。好吧,事实证明这不是真的,export
关键字正是使您能够“使用”接口而不是必须显式 import
接口的成败因素。
因此,它在 TypeScript 中的工作方式如下:
如果您想要一个 interface/type,您可以简单地使用而无需将其导入到使用模块中,则所述接口必须驻留在 .ts
或理想情况下 .d.ts
文件中 同一文件中没有任何导入或导出。这是最重要的,因为一旦您从同一个文件中导出至少一个东西,它就会变成一个模块,并且该模块中的所有内容都必须随后由消费者导入。
举个例子,我们假设您想要一个名为 Dictionary
的类型,您希望能够在不导入的情况下使用该类型。声明方式如下:
// types.d.ts
interface Dictionary {}
interface Foo {}
interface Bar {}
要使用它,您只需执行以下操作:
// consumer.ts
const dict: Dictionary = {};
但是,如果出于某种原因导出该文件中 interfaces/types 的 any,它将不再起作用,例如:
// 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
接口放在 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
N.B.
正如@dtabuenc 在他的回答中提到的,环境模块(.d.ts
文件)是不受欢迎的,我的更正不应被视为建议。这只是试图解释普通模块和环境模块在 TypeScript 中的工作方式。
"Magically available interfaces" 或全局类型是非常不鼓励的,应该主要留给遗产。此外,您不应为正在编写的代码使用环境声明文件(例如 d.ts
文件)。这些是为了stand-in外部non-typescript代码的位置(本质上是将打字稿类型填充到js代码中,以便您可以更好地将其与javascript集成)。
对于您编写的代码,您应该使用纯 .ts
文件来定义您的接口和类型。
虽然不鼓励使用全局类型,但您的问题的答案是 Typescript 中有两种类型的 .ts
文件。这些称为 scripts
和 modules
。
script
中的任何内容都是全局的。因此,如果您在脚本中定义接口,它将在整个应用程序中全局可用(只要脚本通过 ///<reference path="">
标记或通过 files:[]
或 includes:[]
包含在编译中或tsconfig.json
.
中的默认 **/*.ts
另一种文件类型是 'module',module
中的任何内容都是模块私有的。如果您从一个模块中导出任何内容,如果其他模块选择导入它,那么其他模块也可以使用它。
什么使 .ts
文件成为 "script" 或 "module"?好吧……如果您在文件中的任何地方使用 import/export
,该文件就会变成 "module"。如果没有 import/export
语句那么它是一个全局脚本。
我的猜测是您在声明中无意中使用了 import
或 export
并将其放入模块中,这将您在该模块中的所有接口变为私有。如果您希望它们是全局的,那么您将确保您没有在文件中使用 import/export 语句。
我目前正在开发一个使用 Webpack2 和 TypeScript 的 ReactJS 项目。除了一件事之外,一切都完美无缺 - 我无法找到一种方法将我自己编写的接口移动到单独的文件中,以便它们对整个应用程序可见。
出于原型制作的目的,我最初在使用它们的文件中定义了接口,但最终我开始添加多个 类 中需要的接口,这就是所有问题开始的时候。无论我对我的 tsconfig.json
做了什么更改,无论我把文件放在哪里,我的 IDE 和 Webpack 都会抱怨无法找到名称("Could not find name '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'
但这对我来说不合适,因为这不是我想要的需要使用。我希望这些接口在我的应用程序中 'magically' 可用。
这是一个示例 app.d.ts
文件:
interface IAppStateProps {}
interface IAppDispatchProps {}
interface IAppProps extends IAppStateProps, IAppDispatchProps {}
我需要 export
他们还是 declare
?我希望我不必将它们包装在命名空间中?!
更新(2020 年 10 月)
看到这个问题仍然非常受欢迎,我想更详细地解释一下解决方案。
首先,可能而且应该让人们感到困惑的是,我在问题末尾给出的界面示例实际上没有任何 export
关键字,尽管我几乎可以肯定我确实有在提出问题时,它们在我的文件中。我相信我没有将它们包括在问题中,认为它们没有任何区别,无论它们是否存在。好吧,事实证明这不是真的,export
关键字正是使您能够“使用”接口而不是必须显式 import
接口的成败因素。
因此,它在 TypeScript 中的工作方式如下:
如果您想要一个 interface/type,您可以简单地使用而无需将其导入到使用模块中,则所述接口必须驻留在 .ts
或理想情况下 .d.ts
文件中 同一文件中没有任何导入或导出。这是最重要的,因为一旦您从同一个文件中导出至少一个东西,它就会变成一个模块,并且该模块中的所有内容都必须随后由消费者导入。
举个例子,我们假设您想要一个名为 Dictionary
的类型,您希望能够在不导入的情况下使用该类型。声明方式如下:
// types.d.ts
interface Dictionary {}
interface Foo {}
interface Bar {}
要使用它,您只需执行以下操作:
// consumer.ts
const dict: Dictionary = {};
但是,如果出于某种原因导出该文件中 interfaces/types 的 any,它将不再起作用,例如:
// 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
接口放在 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
N.B.
正如@dtabuenc 在他的回答中提到的,环境模块(.d.ts
文件)是不受欢迎的,我的更正不应被视为建议。这只是试图解释普通模块和环境模块在 TypeScript 中的工作方式。
"Magically available interfaces" 或全局类型是非常不鼓励的,应该主要留给遗产。此外,您不应为正在编写的代码使用环境声明文件(例如 d.ts
文件)。这些是为了stand-in外部non-typescript代码的位置(本质上是将打字稿类型填充到js代码中,以便您可以更好地将其与javascript集成)。
对于您编写的代码,您应该使用纯 .ts
文件来定义您的接口和类型。
虽然不鼓励使用全局类型,但您的问题的答案是 Typescript 中有两种类型的 .ts
文件。这些称为 scripts
和 modules
。
script
中的任何内容都是全局的。因此,如果您在脚本中定义接口,它将在整个应用程序中全局可用(只要脚本通过 ///<reference path="">
标记或通过 files:[]
或 includes:[]
包含在编译中或tsconfig.json
.
**/*.ts
另一种文件类型是 'module',module
中的任何内容都是模块私有的。如果您从一个模块中导出任何内容,如果其他模块选择导入它,那么其他模块也可以使用它。
什么使 .ts
文件成为 "script" 或 "module"?好吧……如果您在文件中的任何地方使用 import/export
,该文件就会变成 "module"。如果没有 import/export
语句那么它是一个全局脚本。
我的猜测是您在声明中无意中使用了 import
或 export
并将其放入模块中,这将您在该模块中的所有接口变为私有。如果您希望它们是全局的,那么您将确保您没有在文件中使用 import/export 语句。