模块和命名空间,听起来都是用来组织代码的,但在TypeScript的语境下,它们代表了两种不同的时代和思路。今天咱们把这事儿捋清楚,让你知道该在什么时候用哪个,别再把它们搞混了。
![图片[1]-如何声明模块和命名空间?-速码派](http://www.sumapai.com/wp-content/uploads/2026/01/d05c77e37d0d412aa2498accee46c343_tplv-tb4s082cfz-aigc_resize_1080_1080-1024x683.webp)
先说模块,这是现代JavaScript和TypeScript的绝对主角。一个文件就是一个模块,你用export把东西扔出去,在另一个文件里用import把它拿进来。这玩意儿是ES标准,运行时有实实在在的支持(比如在Node.js或打包后的浏览器环境里)。在TypeScript里为这种模块写类型声明,最常见的就是给没有类型的第三方JS库“打补丁”。
// 假设有一个老旧的JS库叫 "legacy-lib"
declare module 'legacy-lib' {
export function doSomething(value: string): number;
export const version: string;
}
你把这个声明放在一个.d.ts文件里,之后在代码里import { doSomething } from 'legacy-lib',类型检查就生效了。这就是模块声明,核心是描述一个外部模块对外暴露了什么。
那命名空间又是什么鬼?它有个更老的名字叫“内部模块”,是TypeScript在ES模块标准还没普及之前,自己搞出来解决全局作用域污染问题的方案。它用namespace(以前也叫module)关键字把一堆类型、变量、函数包裹起来,形成一个独立的作用域。
namespace MyUtilities {
export function format(str: string): string {
return str.trim();
}
export const MAX_SIZE = 100;
}
要用里面的东西,你得通过命名空间名去访问,比如MyUtilities.format('hello')。这玩意儿在现代前端开发里已经很少见了,因为它会生成一堆全局变量或者复杂的IIFE代码,和现代模块化打包的思想不太搭。但是,在一种场景下它还活着——为一些古老的、以全局变量形式暴露的库写类型定义,或者在某些.d.ts声明文件中组织复杂的类型。
比如说,jQuery这种库,它通常是通过<script>标签引入,在全局挂一个$变量。为它写类型声明,用命名空间就挺合适:
declare namespace jQuery {
function ajax(url: string): void;
const version: string;
}
declare const $: jQuery;
这样,你在代码里用$.ajax('/api'),类型就对了。你看,命名空间在这里是用来描述一个全局对象的结构。
所以,到底怎么选?记住一个简单的原则:如果你在写应用代码,或者给一个使用import/export的库写类型,百分百用ES模块。命名空间只在一种情况下考虑:你需要为那些在全局作用域里暴露对象的古老脚本库(或者某些特殊的运行时环境)编写类型声明文件。
现在新的项目,基本可以忘掉namespace这个东西了。把你的代码拆分成一个个用export/import连接的文件,让打包工具去处理依赖,这才是正道。偶尔在.d.ts文件里看到declare namespace,知道它是在描述一个全局结构就行,别在新代码里模仿它。



























暂无评论内容