TypeScript系列 进阶篇 (五) Namespaces
在TypeScript 1.5之前的版本,有着内部模块Internal modules(使用module { }的形式来声明) 和外部模块External modules的概念。而从 1.5 版本开始,这两个概念的命名发生了变化。原本的Internal modules更改为Namespaces(命名空间),声明方式也相应替换为了namespace { },而External modules则更改为我们现在熟知的模块Modules。使用命名空间,可以自主定义对外可见/不可见的类型或值,能够极大地避免全局命名冲突的问题。我们使用export关键字来对外暴露相应的类型 / 值。
一、以官方提供的 Validators为例体验Namespaces
1 | namespace Validation { |
一开始不理解为什么要使用命名空间,使用一个简简单单的object不好吗?直到读完官方给的示例,发现在namespace里面自由定义类型或值,不导出的内容就成了私有的内容,这种感受可钛上头了。
二、多文件命名空间
在之前的文章TypeScript 系列 进阶篇:(三) 声明合并中,我介绍过关于namespace声明合并的相关内容,namespace的声明合并使多文件命名空间成为可能。我们知道,随着项目体积的增大,文件数量越来越多,namespace的内容也会越来越大,将所有内容写在一个namespace文件中,显然不太明智,因此,随之产生了命名空间的跨文件问题。TS 支持将同一个namespace拆分到多个文件中,而能够保持犹如在同一个文件中定义时的相同的用法。当然,这需要在文件的开头使用 三斜杠指令 /// <reference path="xxx" />来指定路径,告诉编译器文件之间的依赖关系,注意 三斜杠指令应位于文件的开头,关于三斜杠指令的细节,如果有时间我会另出一篇来介绍,本文只关注namespace的内容。下面继续以官方的示例来演示,将上面的validator的栗子拆分到多个文件中。
- 首先,拆分 Base 部分,在
validation.ts中定义基础命名空间Validation:
1 | // validation.ts |
- 拆分仅限字母的校验器部分:在
lettersOnlyValidator.ts中,在文件开头使用三斜杠指令引入validation.ts并随后进行声明合并:
1 | // 使用三斜杠指令来引入Validation.ts |
- 继续扩充:在
ZipCodeValidator.ts中,在文件开头通过 三斜杠指令 来引入Validation.ts:
1 | // ZipCodeValidator.ts |
现在我们的命名空间Validation已经合并好了,但是需要注意,当我们在其它文件中使用相应的导出成员时,依然要使用 三斜杠指令 来引入相关的namespace文件,如在测试文件test.ts中:
1 | // test.ts |
可见,我们把一个混乱的namespace按功能点拆分到不同的文件中,但是除了需要使用三斜杠指令来引入之外,依然保持使用方法不变,后期维护的话就方便多啦!
当然,现在涉及到了多个文件,因此,我们需要确保所有被编译的代码都被加载。这里主要有两种方式:
单文件输出:
通过配置
outputFile项来使指定的文件(包含其依赖文件)被编译输出为单个js文件。1
npx tsc --outFile sample.js test.ts
如上,该命令会将
test.ts文件以及其通过三斜杠指令引入的三个文件,按引入顺序来编译输出在一个sample.js文件中,如此一来当test.ts加载时,便可确保所有被编译的代码都被加载。我们也可以手动列举相应要编译输出的文件,但是显然会更麻烦:1
npx tsc --outFile sample.js Validation.ts LettersOnlyValidator.ts ZipCodeValidator.ts Test.ts
事实上,在现代羡慕中,我们多半使用
vue或react等框架,在这些项目的配置文件中,往往都已经帮我们配置好了输出为单个js文件。多文件输出:
多文件输出为默认选项。使每个被编译的文件都单独输出一个相应的
js文件。这时,我们就需要在相应的html文件中,按照顺序使用<script src="..."></script>来引入相应的js文件。1
2
3
4<script src="Validation.js" type="text/javascript" /></script>
<script src="LettersOnlyValidator.js" type="text/javascript" /></script>
<script src="ZipCodeValidator.js" type="text/javascript" /></script>
<script src="Test.js" type="text/javascript" /></script>
三、别名
可能你也注意到,我们在外部访问命名空间内暴露的成员时,需要带上命名空间本身的名字,尽管现代编辑器很只能,但我们仍然希望有更简单的写法。好在,TS 给我们提供了一种语法,import newName = X.y.z来为命名空间暴露的成员起一个别名。注意,这不要和模块的导入语法import q = require(X)相混淆,这俩不是同一个东西。命名空间的import newName = X.y.x只是单纯地给命名空间的成员起一个别名而已。
1 | // 官方示例的第一个嵌套namespace |
四、环境命名空间 Ambient Namespaces
这部分内容先挖个坑,因为涉及到 环境 ambient,我们通常吧那些没有定义实现的声明,叫做 ambient,这些声明往往出现在 .d.ts拓展名的文件中 (决定了,下一期就讲这个)。我们知道,有些库并不是用TS写的,而是用的JS,因此,我们需要声明这些库暴露的API。而大多数JS库所暴露的都是一个顶级的object,所以很适合使用namespace来表示。下面以 D3这个库为例演示在环境命名空间中定义第三方库的形状:
1 | // 注意 '环境' 的声明都需要添加declare关键字 |
环境命名空间看得云里雾里?没关系,下一篇,风里雨里,声明文件 里等你!
