[中文] https://www.tslang.cn/docs/handbook/namespaces.html
[英文] https://www.typescriptlang.org/docs/handbook/namespaces.html
对照着看疗效更好
基于接口的多种实现
下面提供了两段代码
- 第一段是定义了 StringValidator 接口,然后在两个 class 中实现了它,目的是使用 Validator 校验字符。
- 第二段是playground处理后的代码,可以看到类型描述相关的内容都不会被保留。 ```json interface StringValidator { isAcceptable(s: string): boolean; }
let lettersRegexp = /^[A-Za-z]+$/; let numberRegexp = /^[0-9]+$/;
class LettersOnlyValidator implements StringValidator { isAcceptable(s: string) { return lettersRegexp.test(s); } }
class ZipCodeValidator implements StringValidator { isAcceptable(s: string) { return s.length === 5 && numberRegexp.test(s); } }
// Some samples to try let strings = [“Hello”, “98052”, “101”];
// Validators to use let validators: { [s: string]: StringValidator; } = {}; validators[“ZIP code”] = new ZipCodeValidator(); validators[“Letters only”] = new LettersOnlyValidator();
// Show whether each string passed each validator
for (let s of strings) {
for (let name in validators) {
let isMatch = validators[name].isAcceptable(s);
console.log('${ s }' ${ isMatch ? "matches" : "does not match" } '${ name }'.
);
}
}
```json
let lettersRegexp = /^[A-Za-z]+$/;
let numberRegexp = /^[0-9]+$/;
class LettersOnlyValidator {
isAcceptable(s) {
return lettersRegexp.test(s);
}
}
class ZipCodeValidator {
isAcceptable(s) {
return s.length === 5 && numberRegexp.test(s);
}
}
// Some samples to try
let strings = ["Hello", "98052", "101"];
// Validators to use
let validators = {};
validators["ZIP code"] = new ZipCodeValidator();
validators["Letters only"] = new LettersOnlyValidator();
// Show whether each string passed each validator
for (let s of strings) {
for (let name in validators) {
let isMatch = validators[name].isAcceptable(s);
console.log(`'${s}' ${isMatch ? "matches" : "does not match"} '${name}'.`);
}
}
为什么要使用命名空间
上述代码中,我们增加了 interface、class、let变量。随着更多的Validator,会催生更多的class 和 let变量。为了(1)防止变量和其他对象的冲突(2)有个统一的地方对代码进行组织,我们定义了命名空间namespace,可以这么通俗地理解它:使用namespace定义了一个对象,所有相关的属性、方法都通过这个对象的名称才能访问到。
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
const lettersRegexp = /^[A-Za-z]+$/;
const numberRegexp = /^[0-9]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
var Validation;
(function (Validation) {
const lettersRegexp = /^[A-Za-z]+$/;
const numberRegexp = /^[0-9]+$/;
class LettersOnlyValidator {
isAcceptable(s) {
return lettersRegexp.test(s);
}
}
Validation.LettersOnlyValidator = LettersOnlyValidator;
class ZipCodeValidator {
isAcceptable(s) {
return s.length === 5 && numberRegexp.test(s);
}
}
Validation.ZipCodeValidator = ZipCodeValidator;
})(Validation || (Validation = {}));
为外部类库撰写声明文件
不是用TypeScript书写的外部类库,大部分都是通过提供顶级对象的方式使用的,因此使用命名空间来表示它们是一个好办法。例如D3的声明文件可以这么写:
declare namespace D3 {
export interface Selectors {
select: {
(selector: string): Selection;
(element: EventTarget): Selection;
};
}
export interface Event {
x: number;
y: number;
}
export interface Base extends Selectors {
event: Event;
}
}
declare var d3: D3.Base;
历史和不规则的翻译术语
external modules,ES6使用的模块,简称 modules
internal modules,即命名空间,在TS中刚开始使用 module 表示,后用 namespace 表示。
ambient namespace,翻译为外部命名空间,即通过declare定义的namespace,可以全局引用。
通过上面的例子可以看到,internal namespace,既包含了类型、又包含了实现,实现部分会编译成代码;而 ambient namespace 则通常只包含类型声明,不会编译成代码。
因为 internal namespace 的目的是不让变量污染外部,因此要能通过 句柄 访问的声明,必须通过 export 暴露;而 ambient namespace 只是用于描述类型,则无需 export 也照旧可以使用。
仍旧让人困惑的地方 🤔
对于internal modules,编译出来的代码相当于用var定义的全局变量。
但是在前端工程项目中,它不会被作为全局变量暴露出来,即我们在模块中定义使用,都仅限于该模块能感知到。
这就让internal modules的应用场景很难确定?因为:
- 如果你想要用全局的类型声明,直接 ambient namespace即可,不用包含实现
- 如果你先要一些全局的函数实现和配置,在项目中通过import模块和 webpack注入环境变量都可以实现
如果一个文件中不包含import,不管namespace定义的是类型,在其他TS文件中,是能感知到 namespace 的存在而直接使用的。这让 multi-file-namespaces 这一节的内容,少了具体的应用场景,即在什么场景下要用 <reference
,或许是为了tsc将某个文件编译成js的过程,也需要编译以来的其他文件时?
另外,此前学习有过这么一段记录 “使用命名空间下实际上是创建了全局闭包,因此建议不要和模块一起使用,而是当作全局模块,tsc 生成 .js
文件后,通过在html中引用的方式使用” 它的来源出处在哪里呢?
困惑的主题关键字:
- 三斜杆指令 https://www.tslang.cn/docs/handbook/triple-slash-directives.html
- 声明合并
- 命名空间联合