为什么ES2015模块语法比自定义的TypeScript命名空间更受欢迎?
为什么ES2015模块语法比自定义的TypeScript命名空间更受欢迎?
自定义的TypeScript模块(
module foo {}
)和命名空间(namespace foo {}
)被认为是组织TypeScript代码的过时方法。现在更推荐使用ES2015模块语法(import
/export
)。
来自TypeScript的文档:
模块可以包含代码和声明。
模块还依赖于模块加载器(如CommonJs/Require.js)或支持ES模块的运行时。模块提供更好的代码复用,更强的隔离性和更好的工具支持以进行捆绑。
值得注意的是,对于Node.js应用程序,模块是默认选项,我们建议在现代代码中使用模块而不是命名空间。
我提出这个问题的目的是为了弄清楚为什么优先使用模块语法而非命名空间。
到目前为止,我已经做了以下工作:
PROJECT_TYPES.d.ts
我有多个d.ts
文件,在其中声明包含我在项目源文件中使用的类型的命名空间。
declare namespace MY_NAMESPACE { type FOO = "FOO" }
这样做,我可以在项目的任何源文件中进行如下操作:
someFile.ts
const foo: MY_NAMESPACE.FOO = "FOO";
而且它可以在不需要任何import
/export
的情况下工作。
注意事项:
- 我使用
d.ts
文件是因为这些文件不包含任何import
/export
代码。 - 我使用
declare namespace
而不是仅使用namespace
,因为如果没有declare
关键字,我会收到@typescript-eslint/no-unused-vars
警告。
考虑到MY_NAMESPACE
是一个唯一的名称,这是否被认为是一种不好的做法?在这种情况下,我是否应该使用模块
而非命名空间
?我是否不应该使用d.ts
文件?
我的意思是,TypeScript文档提到:
模块提供更好的代码复用,更强的隔离性和更好的工具支持以进行捆绑。
但是我认为我通过上述描述的模式并没有失去这些好处,因为我所有的“真正”的编译结果代码都被分隔在模块中。但为什么我的类型应该仅在模块中?
为什么ES2015模块语法比自定义的TypeScript命名空间更受欢迎?
TypeScript的namespace
主要用于为代码提供逻辑分组,但ES2015模块的引入使得我们可以通过文件系统来实现这一目的,而不会污染全局作用域。它还在使用类型时提供了更大的灵活性,我们可以使用目录结构来推断命名空间。
在ES2015之前,我们需要使用命名空间来组织代码,如下所示:
// src/geometry/types.d.ts declare namespace Geometry{ export type Shape = 'circle' | 'square' } // src/geometry/main.ts let myShape: Geometry.Shape = 'square'
而在ES2015之后,我们可以使用ES2015模块来实现相同的逻辑分组,如下所示:
// src/geometry/types.ts export type Shape = 'circle' | 'square' // src/geometry/main.ts import { Shape } from './types' let myShape: Shape = 'square' //src/geometry/index.ts export * from './types' export * from './main' //src/index.ts export * as Geometry from './geometry'
在ES2015模块中,我们不再需要使用Geometry
前缀,因为我们已经在geometry
目录中。这种方式更为直观和简洁。
ES2015模块语法比自定义的TypeScript命名空间更受欢迎的原因是它通过文件系统实现了更好的逻辑分组,避免了全局作用域的污染,并提供了更大的灵活性。
参考资料:
- [Typescript Docs - tradeoffs and pitfalls](https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html)
- [SO Answer - when to use namespaces](https://stackoverflow.com/questions/30357634)