在TypeScript中声明一个模块,最早是使用namespace和module的语法,后来才支持es模块。类型和变量将通过import导入并通过export导出。比如你写一个库,导出的变量叫guang,它下面有name和age两个属性,那么声明类型是这样的:exportdefaultGuang;declarenamespaceGuang{exportconstname='guang';exportconstage='20';}使用时使用import导入:importGuangfrom'xxx';console.log(Guang.name,Guang.age);这没问题。但是如果这个库除了支持es模块之外还支持umd呢?UMD规范想必大家都很熟悉,就是判断是CMD、AMD还是全局变量的方式,然后使用合适的模块规范导出模块的值:但是不包括es模块,因为它不是api而是语法。那么如果构建umd规范的代码,用户以脚本的形式导入:我们还能这样做类型提示和检查吗?不会,因为你是用esm的export来导出的,只有import才会有类型提示和相应的检查。那我们该怎么办呢?使用declareglobal声明为全局类型?declareglobal{namespaceGuang{exportconstname='guang';exportconstage='20';}}这样可以解决问题,但是可以不用import直接在esm模块中使用,而我们要在esm中使用import的情况下使用,其他情况下使用globaltypes。有什么办法可以限制在esm中只能用于import,而在其他地方可以作为全局类型使用?TypeScript专门针对这种情况设计了一种语法,叫做exportasnamespacexxx;例如上面的代码可以这样写:export=Guang;exportasnamespaceGuang;declarenamespaceGuang{exportconstname='guang';exportconstage='20';}export=Guang与旧的tsimport语法兼容。要支持umd,必须加上这一行,然后加上exportasnamespaceGuang;sothatyoucanuseitasaglobaltypeinnon-esm:在esm中,如果这样使用,会报错:说你在esm模块中使用了UMD全局类型,推荐改为使用导入。如果使用import引入这个类型,不会报错:这就是为什么它比declareglobal更好的原因。在esm中可以强制导入,非es模块可以作为全局类型使用。这样就完美兼容了esm和umd这两种模块导入方式。而如果你不想这个限制,你也可以在tsconfig.json中关闭它。有一个allowUmdGlobalAccess的编译选项来控制是否支持在es模块中使用UMD全局类型:默认为false,启用后在es模块中使用UMD全局类型不会报错:很多库需要为了兼容esm和umd的使用会这样使用,比如react:所以,如果你开发的库需要支持esm和umd,可以使用exportnamespaceasxxx来导出,比declare好全球的。综上所述,所有的TypeScript模块都是以es模块的形式导入的,但是有些包支持umd,它们可能会以各种方式导入模块。为了实现umd模块的类型检查,可以使用declareglobal将导出的变量改成Global。但是在es模块中,还是要用import来导入,非es模块用的是全局类型,所以更好的办法是用exportasnamespacexxx。这样声明的类型在非esm中使用时,会作为全局类型使用。在esm中,如果直接引用global类型,会报错。建议导入。这是比声明全局更好的地方。当然你也可以将allowUmdGlobalAccess的编译选项设置为true来解除这个约束。支持umd的库以这种方式响应导出类型。如果你想开发一个支持umd的库,不妨试试exportasnamespace。
