学习呦
14 10月 2025 11 min read

命名空间+namespace

Written by 米豆

命名空间在ts里还有些历史遗留问题,ts1.5之前,那会儿ES6还没出来,没有原生的模块化支持,那会儿的namespace被称之为内部模块(Internal modules)

ts中为了复用代码,更好的组织代码,会使用namespace声明,这个东西和ts中的module模块还不太一样,要注意⚠️

语法:

  • 声明要在最外层,⚠️不要再在namespace前加一个export导出语法,如果使用了就要用模块的导入语法

  • 内部的属性要暴漏给外部,可以使用export导出

来来来,看例子

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);
}
}
}

Validation命名空间业务上做一些校验,zip包的校验,输入框仅字母的校验等。内部的两个正则是隐藏的,剩下的类默认是暴漏的

如何使用?

// Some samples to try
let strings = ["Hello", "98052", "101"];
// Validators to use
let validators: { [s: string]: Validation.StringValidator } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();
// Show whether each string passed each validator
for (let s of strings) {
for (let name in validators) {
console.log(
`"${s}" - ${
validators[name].isAcceptable(s) ? "matches" : "does not match"
} ${name}`
);
}
}

PS. 这个代码块和上面的代码块是一个,所以不用导入命名空间,如果声明的命名空间是一个单独的文件,则需要看下面的

多文件的namespace

多文件的namespace就是每一个文件一个namespace,这些文件的关系有两种方式来描述

第一种:特殊标签<reference path="" />

这些namespace文件使用一种标签<reference path="" />(reference标签)来说明之间的关联

首先要有一个主namespace

例如

namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}

其他的namespace文件要使用reference标签标明自己和Validation.ts文件的关系

/// <reference path="Validation.ts" />
namespace Validation {
const lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
}

看line1 的代码,指明了要和Validation.ts文件的关系,最后会自动合并

在测试的文件里

/// <reference path="Validation.ts" />
/// <reference path="LettersOnlyValidator.ts" />

// Some samples to try
let strings = ["Hello", "98052", "101"];
// Validators to use
let validators: { [s: string]: Validation.StringValidator } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();
// Show whether each string passed each validator
for (let s of strings) {
for (let name in validators) {
console.log(
`"${s}" - ${
validators[name].isAcceptable(s) ? "matches" : "does not match"
} ${name}`
);
}
}

主文件也要指明引入的这些命名空间的关系

之后要用tsc的--outFile选项指明 输出文件 和 主文件

tsc --outFile sample.js Test.ts

如果不想用reference标签,就要手动指明所有命名空间文件在编译时

tsc --outFile sample.js LettersOnlyValidator.ts Test.ts

就可以把Test.ts开头的标签声明去掉了

第二种:别名Aliases

还需要再查查资料才能再写这一段,建设中

使用import关键字,可以声明一个特殊的symbol变量,可以作为引入命名空间或者其下变量的别名

这个和模块的import还有点区别,但是命名空间也是可以使用模块导入的,前提得导出

例子

namespace Shapes {
export namespace Polygons {
export class Triangle {}
export class Square {}
}
}
import polygons = Shapes.Polygons;
let sq = new polygons.Square(); // Same as 'new Shapes.Polygons.Square()'

声明namespace (Ambient namespace declaration)

野生声明namespace,也就是既没有用模块导入,也没有用refrence标签、import别名指明定义namesapce的.d.ts文件中,使用declare声明关键字或者修饰符在namespace关键字前

这样声明出来的命名空间,就可以被其他文件看见,当然,d.ts文件也得配置在tsconfig.json文件中

declare namespace D3 {
export interface Selectors {
select: {
(selector: string): Selection;
(element: EventTarget): Selection;
};
}
}

namespace和modules

目的都是让项目结构更清晰,让代码可读性更好,复用性更好

ES Module、Common JS

现在的前端脚手架或者构建工具都使用ESM,ES6模块支持。所以ts的namespace用的比较的少了,官方handbook也推荐使用ESM

而且,namespace仅是ts的特性,在js中也用不了,而且原生支持的东西,为什么还要用个别的东西替代掉呢?

↑