为何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
相关产品推荐
相关产品推荐

