TypeScript 模块
<p><strong> 关于术语的一点说明:</strong> 请务必注意一点,TypeScript 1.5里术语名已经发生了变化。 “内部模块”现在称做“命名空间”。 “外部模块”现在则简称为“模块”,这是为了与 ECMAScript 2015里的术语保持一致,(也就是说 <code>module X {</code> 相当于现在推荐的写法 <code>namespace X {</code>)。</p><h1> 介绍</h1>
<p> 从ECMAScript 2015开始,JavaScript引入了模块的概念。TypeScript也沿用这个概念。</p>
<p> 模块在其自身的作用域里执行,而不是在全局作用域里;这意味着定义在一个模块里的变量,函数,类等等在模块外是不可见的,除非你明确地使用<code>export命令</code>导出它们。 相反,如果想使用其它模块导出的变量、函数、类、接口等,则必须要导入它们,可以使用 <code>import命令</code>。</p>
<p> 模块是自声明的;两个模块之间的关系是通过在文件级别上使用import和export建立的。</p>
<p> 在模块中,模块使用模块加载器去导入其它的模块。 在运行时,模块加载器的作用是在执行此模块代码前去查找并执行这个模块的所有依赖。 大家最熟知的JavaScript模块加载器是服务于Node.js的 CommonJS和服务于Web应用的Require.js。</p>
<p> TypeScript与ECMAScript 2015一样,任何包含顶级<code>import</code>或者<code>export</code>的文件都被当成一个模块。相反,如果一个文件不带有顶级的<code>import</code>或者<code>export</code>声明,那么它的内容被视为全局可见的(因此对模块也是可见的)。</p>
<h1> 导出模块</h1>
<h2> 导出声明</h2>
<p> 任何声明(比如变量,函数,类,类型别名或接口)都能够通过添加<code>export</code>关键字来导出。</p>
<h5>Validation.ts</h5>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">export interface StringValidator {
isAcceptable(s: string): </span><span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<h5>ZipCodeValidator.ts</h5>
<div class="cnblogs_code">
<pre>export const numberRegexp = /^+$/<span style="color: rgba(0, 0, 0, 1)">;
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> s.length === 5 &&<span style="color: rgba(0, 0, 0, 1)"> numberRegexp.test(s);
}
}</span></pre>
</div>
<h2> 导出语句</h2>
<p> 导出语句很便利,因为我们可能需要对导出的部分重命名,所以上面的例子可以这样改写:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> s.length === 5 &&<span style="color: rgba(0, 0, 0, 1)"> numberRegexp.test(s);
}
}
export { ZipCodeValidator };
export { ZipCodeValidator as mainValidator };</span></pre>
</div>
<h2> 重新导出</h2>
<p> 我们经常会去扩展其它模块,并且只导出那个模块的部分内容。 重新导出功能并不会在当前模块导入那个模块或定义一个新的局部变量。</p>
<h5>ParseIntBasedZipCodeValidator.ts</h5>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">export class ParseIntBasedZipCodeValidator {
isAcceptable(s: string) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> s.length === 5 && parseInt(s).toString() ===<span style="color: rgba(0, 0, 0, 1)"> s;
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 导出原先的验证器但做了重命名</span>
export {ZipCodeValidator as RegExpBasedZipCodeValidator} from "./ZipCodeValidator";</pre>
</div>
<p> 或者一个模块可以包裹多个模块,并把他们导出的内容联合在一起,通过语法:<code>export * from "module"</code>。</p>
<h5>AllValidators.ts</h5>
<div class="cnblogs_code">
<pre>export * from "./StringValidator"; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> exports interface StringValidator</span>
export * from "./LettersOnlyValidator"; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> exports class LettersOnlyValidator</span>
export * from "./ZipCodeValidator";<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> exports class ZipCodeValidator</span></pre>
</div>
<h1> 导入</h1>
<p> 模块的导入操作与导出一样简单。 可以使用 <code>import</code>关键字来导入其它模块中的导出内容。</p>
<h2> 导入一个模块中的某个导出内容</h2>
<div class="cnblogs_code">
<pre>import { ZipCodeValidator } from "./ZipCodeValidator"<span style="color: rgba(0, 0, 0, 1)">;
let myValidator </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> ZipCodeValidator();</pre>
</div>
<p> </p>
<h2> 对导入内容重命名</h2>
<div class="cnblogs_code">
<pre>import { ZipCodeValidator as ZCV } from "./ZipCodeValidator"<span style="color: rgba(0, 0, 0, 1)">;
let myValidator </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> ZCV();</pre>
</div>
<h2> 将整个模块导入到一个变量,并通过它来访问模块的导出部分</h2>
<div class="cnblogs_code">
<pre>import * as validator from "./ZipCodeValidator"<span style="color: rgba(0, 0, 0, 1)">;
let myValidator </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> validator.ZipCodeValidator();</pre>
</div>
<p> </p>
<h2> 具有副作用的导入模块</h2>
<p> 尽管不推荐这么做,一些模块会设置一些全局状态供其它模块使用。 这些模块可能没有任何的导出或用户根本就不关注它的导出。 使用下面的方法来导入这类模块:</p>
<div class="cnblogs_code">
<pre>import "./my-module.js";</pre>
</div>
<h1> 默认导出</h1>
<p> 每个模块都可以有一个<code>默认</code>导出。 默认导出使用 <code>default</code>关键字标记;并且一个模块只能够有一个<code>默认</code>导出。 需要使用一种特殊的导入形式来导入默认导出。</p>
<p><code> 默认</code>导出十分便利。 比如,像JQuery这样的类库可能有一个默认导出 即<code>jQuery</code>或<code>$</code>,并且我们基本上也会使用同样的名字<code>jQuery</code>或<code>$</code>导出JQuery。</p>
<h5>JQuery.d.ts</h5>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">declare let $: JQuery;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> $;</pre>
</div>
<h5>App.ts</h5>
<div class="cnblogs_code">
<pre>import $ from "JQuery"<span style="color: rgba(0, 0, 0, 1)">;
$(</span>"button.continue").html( "Next Step..." );</pre>
</div>
<p> 类和函数声明可以直接被标记为默认导出。 标记为默认导出的类和函数的名字是可以省略的。</p>
<h5>ZipCodeValidator.ts</h5>
<div class="cnblogs_code">
<pre>export <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class ZipCodeValidator {
static numberRegexp </span>= /^+$/<span style="color: rgba(0, 0, 0, 1)">;
isAcceptable(s: string) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> s.length === 5 &&<span style="color: rgba(0, 0, 0, 1)"> ZipCodeValidator.numberRegexp.test(s);
}
}</span></pre>
</div>
<h5>Test.ts</h5>
<div class="cnblogs_code">
<pre>import validator from "./ZipCodeValidator"<span style="color: rgba(0, 0, 0, 1)">;
let myValidator </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> validator();</pre>
</div>
<p> 或者</p>
<h5>StaticZipCodeValidator.ts</h5>
<div class="cnblogs_code">
<pre>const numberRegexp = /^+$/<span style="color: rgba(0, 0, 0, 1)">;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (s: string) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> s.length === 5 &&<span style="color: rgba(0, 0, 0, 1)"> numberRegexp.test(s);
}</span></pre>
</div>
<h5>Test.ts</h5>
<div class="cnblogs_code">
<pre>import validate from "./StaticZipCodeValidator"<span style="color: rgba(0, 0, 0, 1)">;
let strings </span>= ["Hello", "98052", "101"<span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用函数校验</span>
strings.forEach(s =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(`</span>"${s}" ${validate(s) ? " matches" : " does not match"<span style="color: rgba(0, 0, 0, 1)">}`);
});</span></pre>
</div>
<p><code> 默认</code>导出也可以是一个值</p>
<h5>OneTwoThree.ts</h5>
<div class="cnblogs_code">
<pre>export <span style="color: rgba(0, 0, 255, 1)">default</span> "123";</pre>
</div>
<h5>Log.ts</h5>
<div class="cnblogs_code">
<pre>import num from "./OneTwoThree"<span style="color: rgba(0, 0, 0, 1)">;
console.log(num); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> "123"</span></pre>
</div>
<h1><code> </code></h1>
<h1><code> export =</code> 和 <code>import = require()</code></h1>
<p> CommonJS和AMD都有一个<code>exports对象的概念</code>,它包含了一个模块的所有导出内容。</p>
<p> CommonJS和AMD也支持把<code>exports</code>替换为一个自定义<code>对象</code>, 这种情况下其作用就类似于 es6 语法里的默认导出,即 <code>export default</code>语法了。虽然作用相似,但是 <code>export default</code> 语法并不能兼容CommonJS和AMD的<code>exports</code>。</p>
<p> 为了支持CommonJS和AMD的<code>exports</code>, TypeScript提供了"<code>export ="</code>语法以支持传统的CommonJS和AMD的工作流模型。</p>
<p><code> "export ="</code>语法定义一个模块的导出<code>对象</code>。 可以是类、接口、命名空间、函数或枚举。</p>
<p> 若使用“<code>export =”</code>导出一个模块,则必须使用TypeScript的特定语法<code>import module = require("module")</code>来导入此模块。</p>
<h5>ZipCodeValidator.ts</h5>
<div class="cnblogs_code">
<pre>let numberRegexp = /^+$/<span style="color: rgba(0, 0, 0, 1)">;
class ZipCodeValidator {
isAcceptable(s: string) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> s.length === 5 &&<span style="color: rgba(0, 0, 0, 1)"> numberRegexp.test(s);
}
}
export </span>= ZipCodeValidator;</pre>
</div>
<h5>Test.ts</h5>
<div class="cnblogs_code">
<pre>import zip = require("./ZipCodeValidator"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Some samples to try</span>
let strings = ["Hello", "98052", "101"<span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Validators to use</span>
let validator = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> zip();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Show whether each string passed each validator</span>
strings.forEach(s =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(`</span>"${ s }" - ${ validator.isAcceptable(s) ? "matches" : "does not match"<span style="color: rgba(0, 0, 0, 1)"> }`);
});</span></pre>
</div>
<h1> 生成模块</h1>
<p> 根据编译时指定的模块目标参数,编译器会生成相应的代码供Node.js (CommonJS),Require.js (AMD),UMD,SystemJS或ECMAScript 2015 native modules (ES6)模块加载系统使用,这些模块的区别参见下表</p>
<table border="0">
<tbody>
<tr>
<td>模块名</td>
<td>特点</td>
<td>环境</td>
<td>应用</td>
</tr>
<tr>
<td>CommonJS</td>
<td>
<p>1.模块可以多次加载,但是只会在第一次加载时运行</p>
<p>一次,然后运行结果就被缓存了,以后再加载,就直接</p>
<p>读取缓存结果。要想让模块再次运行,必须清除缓存</p>
<p>2.模块加载会阻塞接下来代码的执行,需要等到模块</p>
<p>加载完成才能继续执行--同步加载</p>
</td>
<td>服务器</td>
<td>
<p>Node.js的模块规范是参照</p>
<p>CommonJS实现的</p>
</td>
</tr>
<tr>
<td>AMD</td>
<td>
<p>1.异步加载</p>
<p>2.管理模块之间的依赖性,便于代码的编写和维护</p>
</td>
<td>浏览器</td>
<td>
<p>requireJS是参照AMD规范</p>
<p>实现的</p>
</td>
</tr>
<tr>
<td>UMD</td>
<td>
<p>兼容AMD和CommonJS规范的同时,还兼容全局引用的</p>
<p>方式</p>
</td>
<td>浏览器或服务器</td>
<td>无</td>
</tr>
<tr>
<td>ES Module</td>
<td>
<p>1.按需加载(编译时加载)</p>
<p>2.import和export命令只能在模块的顶层,不能在代码块</p>
<p>之中(如if语句中),import()语句可以在代码块中实现</p>
<p>异步动态按需动态加载</p>
</td>
<td>浏览器或Node后端服务</td>
<td>ES6的最新语法支持规范</td>
</tr>
</tbody>
</table>
<p> </p>
<p> 想要了解生成代码中 <code>define</code>,<code>require</code> 和 <code>register</code>的意义,请参考相应模块加载器的文档。</p>
<p> 下面的例子说明了导入导出语句里使用的名字是怎么转换为相应的模块加载器代码的。</p>
<h5>SimpleModule.ts</h5>
<div class="cnblogs_code">
<pre>import m = require("mod"<span style="color: rgba(0, 0, 0, 1)">);
export let t </span>= m.something + 1;</pre>
</div>
<h5>AMD / RequireJS SimpleModule.js</h5>
<div class="cnblogs_code">
<pre>define(["require", "exports", "./mod"], <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (require, exports, mod_1) {
exports.t </span>= mod_1.something + 1<span style="color: rgba(0, 0, 0, 1)">;
});</span></pre>
</div>
<h5>CommonJS / Node SimpleModule.js</h5>
<div class="cnblogs_code">
<pre>let mod_1 = require("./mod"<span style="color: rgba(0, 0, 0, 1)">);
exports.t </span>= mod_1.something + 1;</pre>
</div>
<h5>UMD SimpleModule.js</h5>
<div class="cnblogs_code">
<pre>(<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (factory) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">typeof</span> module === "object" && <span style="color: rgba(0, 0, 255, 1)">typeof</span> module.exports === "object"<span style="color: rgba(0, 0, 0, 1)">) {
let v </span>= factory(require, exports); <span style="color: rgba(0, 0, 255, 1)">if</span> (v !== undefined) module.exports =<span style="color: rgba(0, 0, 0, 1)"> v;
}
</span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">typeof</span> define === "function" &&<span style="color: rgba(0, 0, 0, 1)"> define.amd) {
define([</span>"require", "exports", "./mod"<span style="color: rgba(0, 0, 0, 1)">], factory);
}
})(</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (require, exports) {
let mod_1 </span>= require("./mod"<span style="color: rgba(0, 0, 0, 1)">);
exports.t </span>= mod_1.something + 1<span style="color: rgba(0, 0, 0, 1)">;
});</span></pre>
</div>
<h5>System SimpleModule.js</h5>
<div class="cnblogs_code">
<pre>System.register(["./mod"], <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(exports_1) {
let mod_1;
let t;
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> {
setters:[
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (mod_1_1) {
mod_1 </span>=<span style="color: rgba(0, 0, 0, 1)"> mod_1_1;
}],
execute: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
exports_1(</span>"t", t = mod_1.something + 1<span style="color: rgba(0, 0, 0, 1)">);
}
}
});</span></pre>
</div>
<h5>Native ECMAScript 2015 modules SimpleModule.js</h5>
<div class="cnblogs_code">
<pre>import { something } from "./mod"<span style="color: rgba(0, 0, 0, 1)">;
export let t </span>= something + 1;</pre>
</div>
<h1> 简单示例</h1>
<p> 下面我们来整理一下前面的验证器实现,每个模块只有一个命名的导出。</p>
<p> 为了编译,我们必须在命令行上指定一个模块目标。对于Node.js来说,使用<code>--module commonjs</code>; 对于Require.js来说,使用<code>--module amd</code>。比如:</p>
<div class="cnblogs_code">
<pre>tsc --module commonjs Test.ts</pre>
</div>
<p> 编译完成后,每个模块会生成一个单独的<code>.js</code>文件。 好比使用了reference标签,编译器会根据 <code>import</code>语句编译相应的文件。</p>
<h5>Validation.ts</h5>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">export interface StringValidator {
isAcceptable(s: string): </span><span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<h5>LettersOnlyValidator.ts</h5>
<div class="cnblogs_code">
<pre>import { StringValidator } from "./Validation"<span style="color: rgba(0, 0, 0, 1)">;
const lettersRegexp </span>= /^+$/<span style="color: rgba(0, 0, 0, 1)">;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> lettersRegexp.test(s);
}
}</span></pre>
</div>
<h5>ZipCodeValidator.ts</h5>
<div class="cnblogs_code">
<pre>import { StringValidator } from "./Validation"<span style="color: rgba(0, 0, 0, 1)">;
const numberRegexp </span>= /^+$/<span style="color: rgba(0, 0, 0, 1)">;
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> s.length === 5 &&<span style="color: rgba(0, 0, 0, 1)"> numberRegexp.test(s);
}
}</span></pre>
</div>
<h5>Test.ts</h5>
<div class="cnblogs_code">
<pre>import { StringValidator } from "./Validation"<span style="color: rgba(0, 0, 0, 1)">;
import { ZipCodeValidator } from </span>"./ZipCodeValidator"<span style="color: rgba(0, 0, 0, 1)">;
import { LettersOnlyValidator } from </span>"./LettersOnlyValidator"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Some samples to try</span>
let strings = ["Hello", "98052", "101"<span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Validators to use</span>
let validators: { : StringValidator; } =<span style="color: rgba(0, 0, 0, 1)"> {};
validators[</span>"ZIP code"] = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ZipCodeValidator();
validators[</span>"Letters only"] = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> LettersOnlyValidator();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Show whether each string passed each validator</span>
strings.forEach(s =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (let name <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> validators) {
console.log(`</span>"${ s }" - ${ validators.isAcceptable(s) ? "matches" : "does not match"<span style="color: rgba(0, 0, 0, 1)"> } ${ name }`);
}
});</span></pre>
</div>
<h1> 可选的模块加载和其它高级加载场景</h1>
<p> 有时候,你只想在某种条件下才加载某个模块。 在TypeScript里,使用下面的方式来实现它和其它的高级加载场景,我们可以直接调用模块加载器并且可以保证类型完全。</p>
<p> 编译器会检测是否每个模块都会在生成的JavaScript中用到。 如果一个模块标识符只在类型注解部分使用,并且完全没有在表达式中使用时,就不会生成 <code>require</code>这个模块的代码。 省略掉没有用到的引用对性能提升是很有益的,并同时提供了选择性加载模块的能力。</p>
<p> 这种模式的核心是<code>import id = require("...")</code>语句可以让我们访问模块导出的类型。 模块加载器会被动态调用(通过 <code>require</code>),就像下面<code>if</code>代码块里那样。 它利用了省略引用的优化,所以模块只在被需要时加载。 为了让这个模块工作,一定要注意 <code>import</code>定义的标识符只能在表示类型处使用(不能在会转换成JavaScript的地方)。</p>
<p> 为了确保类型安全性,我们可以使用<code>typeof</code>关键字。 <code>typeof</code>关键字,当在表示类型的地方使用时,会得出一个类型值,这里就表示模块的类型。</p>
<h5>示例:Node.js里的动态模块加载</h5>
<div class="cnblogs_code">
<pre>declare <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> require(moduleName: string): any;
import { ZipCodeValidator as Zip } from </span>"./ZipCodeValidator"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (needZipValidation) {
let ZipCodeValidator: </span><span style="color: rgba(0, 0, 255, 1)">typeof</span> Zip = require("./ZipCodeValidator"<span style="color: rgba(0, 0, 0, 1)">);
let validator </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ZipCodeValidator();
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (validator.isAcceptable("...")) { <span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> ... </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> }
}</span></pre>
</div>
<h5>示例:require.js里的动态模块加载</h5>
<div class="cnblogs_code">
<pre>declare <span style="color: rgba(0, 0, 255, 1)">function</span> require(moduleNames: string[], onLoad: (...args: any[]) => <span style="color: rgba(0, 0, 255, 1)">void</span>): <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
import </span>* as Zip from "./ZipCodeValidator"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (needZipValidation) {
require([</span>"./ZipCodeValidator"], (ZipCodeValidator: <span style="color: rgba(0, 0, 255, 1)">typeof</span> Zip) =><span style="color: rgba(0, 0, 0, 1)"> {
let validator </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ZipCodeValidator.ZipCodeValidator();
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (validator.isAcceptable("...")) { <span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> ... </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> }
});
}</span></pre>
</div>
<h5>示例:System.js里的动态模块加载</h5>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">declare const System: any;
import { ZipCodeValidator as Zip } from </span>"./ZipCodeValidator"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (needZipValidation) {
System.import(</span>"./ZipCodeValidator").then((ZipCodeValidator: <span style="color: rgba(0, 0, 255, 1)">typeof</span> Zip) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">var</span> x = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ZipCodeValidator();
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (x.isAcceptable("...")) { <span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> ... </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> }
});
}</span></pre>
</div>
<h1>使用其它的JavaScript库(外部模块)</h1>
<p> 要想描述不是用TypeScript编写的类库的类型,我们需要声明类库所暴露出的API。由于大部分程序库只提供少数的顶级对象,采用外部命名空间来表示是一个好办法。</p>
<p> 我们之所以叫它声明是因为它不是“外部程序”的具体实现。 我们通常是在 <code>.d.ts</code>里写这些声明。 如果你熟悉C/C++,你可以把它们当做 <code>.h</code>文件。 让我们看一些例子。</p>
<p> 在Node.js里,大部分工作是加载一个或多个模块。 我们可以使用顶级的 <code>export</code>声明来为每个模块都定义一个<code>.d.ts</code>文件,但最好还是写在一个大的<code>.d.ts</code>文件里。 我们使用与构造一个外部命名空间相似的方法,但是这里使用 <code>module</code>关键字并且把名字用引号括起来,方便之后进行<code>import操作</code>。 例如:</p>
<h5>node.d.ts (simplified excerpt)</h5>
<div class="cnblogs_code">
<pre>declare module "url"<span style="color: rgba(0, 0, 0, 1)"> {
export interface Url {
protocol</span>?<span style="color: rgba(0, 0, 0, 1)">: string;
hostname</span>?<span style="color: rgba(0, 0, 0, 1)">: string;
pathname</span>?<span style="color: rgba(0, 0, 0, 1)">: string;
}
export </span><span style="color: rgba(0, 0, 255, 1)">function</span> parse(urlStr: string, parseQueryString?, slashesDenoteHost?<span style="color: rgba(0, 0, 0, 1)">): Url;
}
declare module </span>"path"<span style="color: rgba(0, 0, 0, 1)"> {
export </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> normalize(p: string): string;
export </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> join(...paths: any[]): string;
export let sep: string;
}</span></pre>
</div>
<p> 现在我们可以<code>/// <reference></code> <code>node.d.ts</code>并且使用<code>import url = require("url");</code>或<code>import * as URL from "url"</code>加载模块。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">/ <reference path="node.d.ts"/></span>
import * as URL from "url"<span style="color: rgba(0, 0, 0, 1)">;
let myUrl </span>= URL.parse("http://www.typescriptlang.org");</pre>
</div>
<p> </p>
<h3> 外部模块简写</h3>
<p> 假如你不想在使用一个新模块之前花时间去编写声明,你可以采用声明的简写形式以便能够快速使用它。</p>
<h5>declarations.d.ts</h5>
<div class="cnblogs_code">
<pre>declare module "hot-new-module";</pre>
</div>
<p> 简写模块里所有导出的类型将是<code>any</code>。</p>
<h5>math-lib.d.ts</h5>
<div class="cnblogs_code">
<pre>import x, {y} from "hot-new-module"<span style="color: rgba(0, 0, 0, 1)">;
x(y);</span></pre>
</div>
<h3> 模块声明通配符</h3>
<p> 某些模块加载器如SystemJS 和 AMD支持导入非JavaScript内容。 它们通常会使用一个前缀或后缀来表示特殊的加载语法。 模块声明通配符可以用来表示这些情况。</p>
<div class="cnblogs_code">
<pre>declare module "*!text"<span style="color: rgba(0, 0, 0, 1)"> {
const content: string;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> content;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Some do it the other way around.</span>
declare module "json!*"<span style="color: rgba(0, 0, 0, 1)"> {
const value: any;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> value;
}</span></pre>
</div>
<p> 现在你可以就导入匹配<code>"*!text"</code>或<code>"json!*"</code>的内容了。</p>
<div class="cnblogs_code">
<pre>import fileContent from "./xyz.txt!text"<span style="color: rgba(0, 0, 0, 1)">;
import data from </span>"json!http://example.com/data.json"<span style="color: rgba(0, 0, 0, 1)">;
console.log(data, fileContent);</span></pre>
</div>
<h3> UMD模块</h3>
<p> 有些模块被设计成兼容多个模块加载器,或者不使用模块加载器(全局变量)。 它们以 UMD模块为代表。 这些库可以通过导入的形式或全局变量的形式访问。 例如:</p>
<h5>math-lib.d.ts</h5>
<div class="cnblogs_code">
<pre>export <span style="color: rgba(0, 0, 255, 1)">function</span> isPrime(x: number): <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)">;
export as namespace mathLib;</span></pre>
</div>
<p> 之后,这个库可以在某个模块里通过导入来使用:</p>
<div class="cnblogs_code">
<pre>import { isPrime } from "math-lib"<span style="color: rgba(0, 0, 0, 1)">;
isPrime(</span>2<span style="color: rgba(0, 0, 0, 1)">);
mathLib.isPrime(</span>2); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 错误: 不能在模块内使用全局定义。</span></pre>
</div>
<p> 它同样可以通过全局变量的形式使用,但只能在某个脚本(脚本是指不带有模块导入或导出的文件)里。</p>
<div class="cnblogs_code">
<pre>mathLib.isPrime(2);</pre>
</div>
<p> </p>
<h1> 创建模块结构指导</h1>
<h2> 尽可能地在顶层导出</h2>
<p> 用户应该更容易地使用你模块导出的内容。 嵌套层次过多会变得难以处理,因此仔细考虑一下如何组织你的代码。</p>
<p> 从你的模块中导出一个命名空间就是一个增加嵌套的例子。 虽然命名空间有时候有它们的用处,在使用模块的时候它们额外地增加了一层。 这对用户来说是很不便的并且通常是多余的。</p>
<p> 导出类的静态方法也有同样的问题 - 这个类本身就增加了一层嵌套。 除非它能方便表述或便于清晰使用,否则请考虑直接导出一个辅助方法。</p>
<h3> 如果仅导出单个 <code>class</code> 或 <code>function</code>,使用 <code>export default</code></h3>
<p> 就像“在顶层上导出”帮助减少用户使用的难度,一个默认的导出也能起到这个效果。 如果一个模块就是为了导出特定的内容,那么你应该考虑使用一个默认导出。 这会令模块的导入和使用变得些许简单。 比如:</p>
<h4>MyClass.ts</h4>
<div class="cnblogs_code">
<pre>export <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class SomeType {
constructor() { ... }
}</span></pre>
</div>
<h4>MyFunc.ts</h4>
<div class="cnblogs_code">
<pre>export <span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span> getThing() { <span style="color: rgba(0, 0, 255, 1)">return</span> 'thing'; }</pre>
</div>
<h4>Consumer.ts</h4>
<div class="cnblogs_code">
<pre>import t from "./MyClass"<span style="color: rgba(0, 0, 0, 1)">;
import f from </span>"./MyFunc"<span style="color: rgba(0, 0, 0, 1)">;
let x </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> t();
console.log(f());</span></pre>
</div>
<p> 对用户来说这是最理想的。他们可以随意命名导入模块的类型(本例为<code>t</code>)并且不需要多余的(.)来找到相关对象。</p>
<h3> 如果要导出多个对象,把它们放在顶层里导出</h3>
<h4>MyThings.ts</h4>
<div class="cnblogs_code">
<pre>export class SomeType { <span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> ... </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> }
export </span><span style="color: rgba(0, 0, 255, 1)">function</span> someFunc() { <span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> ... </span><span style="color: rgba(0, 128, 0, 1)">*/</span> }</pre>
</div>
<p>相反,当导入的时候:</p>
<h3> 明确地列出导入的名字</h3>
<h4>Consumer.ts</h4>
<div class="cnblogs_code">
<pre>import { SomeType, someFunc } from "./MyThings"<span style="color: rgba(0, 0, 0, 1)">;
let x </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> SomeType();
let y </span>= someFunc();</pre>
</div>
<h3>使用命名空间导入模式当你要导出大量内容的时候</h3>
<h4>MyLargeModule.ts</h4>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">export class Dog { ... }
export class Cat { ... }
export class Tree { ... }
export class Flower { ... }</span></pre>
</div>
<h4>Consumer.ts</h4>
<div class="cnblogs_code">
<pre>import * as myLargeModule from "./MyLargeModule.ts"<span style="color: rgba(0, 0, 0, 1)">;
let x </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> myLargeModule.Dog();</pre>
</div>
<h2> 使用重新导出进行扩展</h2>
<p> 你可能经常需要去扩展一个模块的功能。 JS里常用的一个模式是JQuery那样去扩展原对象。 如我们之前提到的,模块不会像全局命名空间对象那样去 <em>合并</em>。 推荐的方案是 <em>不要</em>去改变原来的对象,而是导出一个新的实体来提供新的功能。</p>
<p> 假设<code>Calculator.ts</code>模块里定义了一个简单的计算器实现。 这个模块同样提供了一个辅助函数来测试计算器的功能,通过传入一系列输入的字符串并在最后给出结果。</p>
<h4>Calculator.ts</h4>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">export class Calculator {
private current </span>= 0<span style="color: rgba(0, 0, 0, 1)">;
private memory </span>= 0<span style="color: rgba(0, 0, 0, 1)">;
private operator: string;
protected processDigit(digit: string, currentValue: number) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (digit >= "0" && digit <= "9"<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> currentValue * 10 + (digit.charCodeAt(0) - "0".charCodeAt(0<span style="color: rgba(0, 0, 0, 1)">));
}
}
protected processOperator(operator: string) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (["+", "-", "*", "/"].indexOf(operator) >= 0<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> operator;
}
}
protected evaluateOperator(operator: string, left: number, right: number): number {
</span><span style="color: rgba(0, 0, 255, 1)">switch</span> (<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.operator) {
</span><span style="color: rgba(0, 0, 255, 1)">case</span> "+": <span style="color: rgba(0, 0, 255, 1)">return</span> left +<span style="color: rgba(0, 0, 0, 1)"> right;
</span><span style="color: rgba(0, 0, 255, 1)">case</span> "-": <span style="color: rgba(0, 0, 255, 1)">return</span> left -<span style="color: rgba(0, 0, 0, 1)"> right;
</span><span style="color: rgba(0, 0, 255, 1)">case</span> "*": <span style="color: rgba(0, 0, 255, 1)">return</span> left *<span style="color: rgba(0, 0, 0, 1)"> right;
</span><span style="color: rgba(0, 0, 255, 1)">case</span> "/": <span style="color: rgba(0, 0, 255, 1)">return</span> left /<span style="color: rgba(0, 0, 0, 1)"> right;
}
}
private evaluate() {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.operator) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.memory = <span style="color: rgba(0, 0, 255, 1)">this</span>.evaluateOperator(<span style="color: rgba(0, 0, 255, 1)">this</span>.operator, <span style="color: rgba(0, 0, 255, 1)">this</span>.memory, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.current);
}
</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.memory = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.current;
}
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.current = 0<span style="color: rgba(0, 0, 0, 1)">;
}
public handleChar(</span><span style="color: rgba(0, 0, 255, 1)">char</span><span style="color: rgba(0, 0, 0, 1)">: string) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">char</span> === "="<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.evaluate();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
let value </span>= <span style="color: rgba(0, 0, 255, 1)">this</span>.processDigit(<span style="color: rgba(0, 0, 255, 1)">char</span>, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.current);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (value !==<span style="color: rgba(0, 0, 0, 1)"> undefined) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.current =<span style="color: rgba(0, 0, 0, 1)"> value;
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
let value </span>= <span style="color: rgba(0, 0, 255, 1)">this</span>.processOperator(<span style="color: rgba(0, 0, 255, 1)">char</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (value !==<span style="color: rgba(0, 0, 0, 1)"> undefined) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.evaluate();
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.operator =<span style="color: rgba(0, 0, 0, 1)"> value;
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
}
}
}
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Error(`Unsupported input: '${char}'<span style="color: rgba(0, 0, 0, 1)">`);
}
public getResult() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.memory;
}
}
export </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> test(c: Calculator, input: string) {
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0; i < input.length; i++<span style="color: rgba(0, 0, 0, 1)">) {
c.handleChar(input);
}
console.log(`result of </span>'${input}' is '${c.getResult()}'<span style="color: rgba(0, 0, 0, 1)">`);
}</span></pre>
</div>
<p> 下面使用导出的<code>test</code>函数来测试计算器。</p>
<h4>TestCalculator.ts</h4>
<div class="cnblogs_code">
<pre>import { Calculator, test } from "./Calculator"<span style="color: rgba(0, 0, 0, 1)">;
let c </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Calculator();
test(c, </span>"1+2*33/11="); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> prints 9</span></pre>
</div>
<p> 现在扩展它,添加支持输入其它进制(十进制以外),让我们来创建<code>ProgrammerCalculator.ts</code>。</p>
<h4>ProgrammerCalculator.ts</h4>
<div class="cnblogs_code">
<pre>import { Calculator } from "./Calculator"<span style="color: rgba(0, 0, 0, 1)">;
class ProgrammerCalculator extends Calculator {
static digits </span>= ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"<span style="color: rgba(0, 0, 0, 1)">];
constructor(public base: number) {
super();
const maxBase </span>=<span style="color: rgba(0, 0, 0, 1)"> ProgrammerCalculator.digits.length;
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (base <= 0 || base ><span style="color: rgba(0, 0, 0, 1)"> maxBase) {
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Error(`base has to be within 0<span style="color: rgba(0, 0, 0, 1)"> to ${maxBase} inclusive.`);
}
}
protected processDigit(digit: string, currentValue: number) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (ProgrammerCalculator.digits.indexOf(digit) >= 0<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> currentValue * <span style="color: rgba(0, 0, 255, 1)">this</span>.base +<span style="color: rgba(0, 0, 0, 1)"> ProgrammerCalculator.digits.indexOf(digit);
}
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Export the new extended calculator as Calculator</span>
<span style="color: rgba(0, 0, 0, 1)">export { ProgrammerCalculator as Calculator };
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Also, export the helper function</span>
export { test } from "./Calculator";</pre>
</div>
<p> 新的<code>ProgrammerCalculator</code>模块导出的API与原先的<code>Calculator</code>模块很相似,但却没有改变原模块里的对象。 下面是测试ProgrammerCalculator类的代码:</p>
<h4>TestProgrammerCalculator.ts</h4>
<div class="cnblogs_code">
<pre>import { Calculator, test } from "./ProgrammerCalculator"<span style="color: rgba(0, 0, 0, 1)">;
let c </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> Calculator(2<span style="color: rgba(0, 0, 0, 1)">);
test(c, </span>"001+010="); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> prints 3</span></pre>
</div>
<h2> 模块里不要使用命名空间</h2>
<p> 当初次进入基于模块的开发模式时,可能总会控制不住要将导出包裹在一个命名空间里。 模块具有其自己的作用域,并且只有导出的声明才会在模块外部可见。 记住这点,命名空间在使用模块时几乎没什么价值。</p>
<p> 在组织方面,命名空间对于在全局作用域内对逻辑上相关的对象和类型进行分组是很便利的。 例如,在C#里,你会从 <code>System.Collections</code>里找到所有集合的类型。 通过将类型有层次地组织在命名空间里,可以方便用户找到与使用那些类型。 然而,模块本身已经存在于文件系统之中,这是必须的。 我们必须通过路径和文件名找到它们,这已经提供了一种逻辑上的组织形式。 我们可以创建 <code>/collections/generic/</code>文件夹,把相应模块放在这里面。</p>
<p> 命名空间对解决全局作用域里命名冲突来说是很重要的。 比如,你可以有一个 <code>My.Application.Customer.AddForm</code>和<code>My.Application.Order.AddForm</code> -- 两个类型的名字相同,但命名空间不同。 然而,这对于模块来说却不是一个问题。 在一个模块里,没有理由两个对象拥有同一个名字。 从模块的使用角度来说,使用者会挑出他们用来引用模块的名字,所以也没有理由发生重名的情况。</p>
<blockquote>
<p>更多关于模块和命名空间的资料查看命名空间和模块</p>
</blockquote>
<h2> 危险信号</h2>
<p>以下均为模块结构上的危险信号。重新检查以确保你没有在对模块使用命名空间:</p>
<ul>
<li>文件的顶层声明是<code>export namespace Foo { ... }</code> (删除<code>Foo</code>并把所有内容向上层移动一层)</li>
<li>文件只有一个<code>export class</code>或<code>export function</code> (考虑使用<code>export default</code>)</li>
<li>多个文件的顶层具有同样的<code>export namespace Foo {</code> (不要以为这些会合并到一个<code>Foo</code>中!)</li>
</ul>
<p> </p><br><br>
来源:https://www.cnblogs.com/fanqisoft/p/12015092.html
頁:
[1]