You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何TypeScript命名空间中部分成员无需export即可外部访问?

为什么express的json无需export即可访问,而Router需要?

这个问题确实有点绕,我当初刚接触TypeScript模块合并的时候也懵过,咱们一步步拆解来看:

1. 先理解express的导出方式:export = e

express的index.d.ts里用了export = e,这是TypeScript专门为兼容CommonJS/AMD模块系统设计的语法——它用来导出单个实体,这里的实体是合并了函数e()和同名命名空间e的对象。当你写import * as express from 'express'时,express就是这个合并后的函数对象。

2. TypeScript的声明合并:函数+同名命名空间

TypeScript有个核心特性叫声明合并:当你在同一个模块里声明一个函数和一个同名的命名空间时,TypeScript会自动把命名空间里的成员合并到函数对象上,成为函数的属性。

这就是为什么express.json能被访问的关键:var json虽然没加export,但它是命名空间e的顶级成员,会被合并到函数e上,所以你可以通过express.json()直接调用它。

3. 命名空间里export的作用:支持单独导入

那为什么Router要加export呢?核心原因是允许单独导入该成员:

  • 没加export的json:只能通过express.json访问,无法用import { json } from 'express'单独导入;
  • 加了export的Router:除了可以通过express.Router()调用,还支持import { Router } from 'express'这种单独导入的写法,给用户提供了更灵活的使用方式。

举个简化的例子验证

假设我们写一个类似的d.ts文件:

declare function foo(): void;
declare namespace foo {
  var bar: string;
  export function baz(): void;
}
export = foo;

当我们导入使用时:

import * as foo from './foo';
console.log(foo.bar); // 正常访问(bar是合并到foo函数的属性)
foo.baz(); // 正常访问
import { baz } from './foo'; // 可以单独导入baz
import { bar } from './foo'; // 报错!因为bar没加export,无法单独导入

这样就能清楚看到两者的区别了——export在这里控制的不是能否通过合并后的对象访问,而是能否被单独导入。

内容的提问来源于stack exchange,提问作者Mike Lippert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:50:20