TypeScript 学习笔记8: Modules

原文链接:https://leanpub.com/essentialtypescript/read#leanpub-auto-modules
TypeScript支持两种模块:内部模块、外部模块。 内部模块就是用 namespace 封装起来的代码块。外部模块就是CommonJS、amd等实现的功能,在TypeScript中定义了更简单的语法。

1. Namespaces

namespace 也是 syntactic sugar, 本质上它也是用 function 定义作用域,《Eloquent JavaScript》的第十章详细的讲了基本原理。直接看用法:

//Model.ts
namespace TodoApp.Model {
    export interface Todo {
        id: number;
        name: string;
        state: TodoState;
    }
}

namespace TodoApp.Model {
    export enum TodoState {
        New = 1,
        Active = 2,
        Complete = 3, 
        Deleted = 4
    }
}

namespace TodoApp.DataAccess {
    import Todo = TodoApp.Model.Todo;

    export interface ITodoService {
        add(todo: Todo): Todo;
        delete(todoId: number): void;
        getAll(): Todo[];
        getById(todoId: number): Todo;
    }
}

说明:
1. namespace 定义一个作用域,和全局空间隔离开来;
2. namespace 的名字 TodoApp.Model,中间有没有“点”都可以,有多少“点”都可以;
3. 一个namespace可以拆分成多个定义,如上面的TodoApp.Model,这不会导致重复定义;它们也可以写在不同的文件中;
4. 多个namespace的定义可以写在一个文件中;
5. 加上 export 前缀,才能在其它namespace中访问,否则,出了它所在的namespace空间,谁也不认识它。如:interface Todo, enum TodoState。
6. 在其它namespace中使用TodoApp.Model.Todo需要写全名,或则,定义别名:import Todo = TodoApp.Model.Todo;
我觉得 import 这个关键字不好,用 alias 会更明确,因为,这里并没有import的作用,只是给 TodoApp.Model.Todo 一个别名。

2. Using namespaces to encapsulate private members

回顾一下前两章讲的 class TodoService,它用到了 static 变量和方法创建新的TodoId,本质上来讲,用 static 方法把 nextId() 方法放到 TodoService 这个类的定义中,是为了把这个功能全局空间分离出来,现在,有了namespace,我们可以这么做:

//DataAccess.ts
namespace DataAccess {
    import Model = TodoApp.Model;
    import Todo = Model.Todo;

    export interface ITodoService {
        add(todo: Todo): Todo;
        delete(todoId: number): void;
        getAll(): Todo[];
        getById(todoId: number): Todo;
    }

    let _lastId: number = 0;

    function generateTodoId(): number {
        return _lastId += 1;
    }

    class TodoService implements ITodoService {        
        constructor(private todos: Todo[]) {        
        }
        add(todo: Todo) {
            todo.id = generateTodoId();
            this.todos.push(todo);
            return todo;
        }
        // ...
    }
}

把 _lastId 和 generateTodoId() 从TodoService 中拿出来,放到 namespace DataAccess 定义的局部空间中。

3. Internal and External Modules

Internal Module:namespace,上一节介绍的方式;
External Module:每个module在自己的文件中,不使用namespace,文件本身就定义了一个局部空间。TypeScript 提供了一种方式,ECMAScript2015也提供了一种方式。无论用哪一种方式,tsc 编译出来的代码是相同的。

4. Switching from internal to external modules

  1. 去掉Model.ts 和 DataAccess.ts 文件中的namespace关键字,及其相关的括号;
  2. 修改 tsconfig.json,指定 module 属性:

    {
        "compilerOptions": {
        "target": "es5",
        "module": "system"
        }
    }
    

    module属性有多个备选值:system, amd, commonjs, es2015 等等。

5. Importing modules using Require syntax

//Model.ts
export interface Todo {
    id: number;
    name: string;
    state: TodoState;
}    

export enum TodoState {
    New = 1,
    Active = 2,
    Complete = 3, 
    Deleted = 4
}
//DataAccess.ts
import Model = require('./Model');
import Todo = Model.Todo;

// 其它代码 ...

说明:
1. Model.ts 是一个文件,也是一个module;
2. Model.ts 中不需要namespace;
3. 每一个需要暴露给外部的接口,都需要加上 export 前缀,如:export interface Todo 和 export enum TodoState
4. DataAccess.ts 中,require(‘./Model’) 导入了Model.ts 模块,不要写文件扩展名;
5. import Model = … ,只是给这个模块定义一个别名(alias),不是必须叫Model,可以是任意名字;

6. Importing modules using ECMAScript 2015 syntax

//DataAccess.ts
import * as Model from './Model';
import Todo = Model.Todo;

// 其它代码 ...

这是es2015 导入模块的语法,只有一行代码不同。
事实上,es2015的导入语法还提供了更丰富的内容。
1. 只导入Todo:

import {Todo} from './Model';

这时,后续的代码不能使用 TodoState。
2. 导入指定内容:

import {Todo, TodoState} from './Model';

导入Todo 和 TodoState,如果Model.ts中还有其它export,不会导入。
3. 导入的同时,指定别名:

import {Todo as TodoTask, TodoState} from './Model';

给Todo指定了一个别名:TodoTask,后续的代码用到Todo时,都要改成TodoTask。
4. 导入模块,但导入它任何 export 的接口

import './Config';

通常很少用到这种方式。当Config.ts模块中有自动执行的代码,这种方式才有用,例如:Config.ts 中有一段代码,用来配置环境变量,而这些代码在加载时就会自动执行。

7. Loading External Modules

ECMAScript 2015 虽然定义了加载模块的语法,但现在还没有一个标准的loader,浏览器并没有内置支持这种语法的loader。多年以来,已经有多个loader被广泛使用,我们必须从中选择一个。我们选择 systemJS 作为模块加载器,因为,它提供了 es2015 的导入模块的语法。这样的话,等将来es2015的load语法被浏览器广泛支持时,我们不用修改代码,就可以使用内置的loader了。
需要在 index.html 中加载 system.js:

<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.22/system.js"></script>

当然,我们也可以把system.js下载到本地。
使用System 引用 app.ts:

<script type="text/javascript">
    System.defaultJSExtensions = true;
    System.import('app')
</script>

这样,当加载index.html 时,app.ts 中的代码就开始执行了。
在app.ts中添加一些代码:

// app.ts
import { Todo, TodoState } from './Model'; 
import { TodoService } from './DataAccess';

let service = new TodoService([]);
service.add({
    id: 1,
    name: 'Pick up drycleaning',
    state: TodoState.New
})

let todos = service.getAll();

todos.forEach(todo =>
    console.log(`${todo.name} [${TodoState[todo.state]}]`)
)

到命令行下,运行:
cd TypeScriptTodo
lite-server
看看浏览器 console 的输出。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TypeScript 是一种由微软开发并维护的开源编程语言。它是 JavaScript 的扩展,为 JavaScript 提供了静态类型检查、类、接口和命名空间等功能。 以下是 TypeScript学习笔记: ## 安装 使用 npm 安装 TypeScript: ``` npm install -g typescript ``` ## 编写 TypeScript 代码 TypeScript 文件的扩展名为 `.ts`。可以使用任何文本编辑器来编写 TypeScript 代码。 以下是一个简单的 TypeScript 代码示例: ```typescript function greeter(name: string) { return "Hello, " + name + "!"; } let user = "John"; console.log(greeter(user)); ``` ## 编译 TypeScript 代码 使用 `tsc` 命令编译 TypeScript 代码。例如,将上面的代码编译为 JavaScript: ``` tsc greeter.ts ``` 这将生成一个名为 `greeter.js` 的 JavaScript 文件。 ## 类型注解 TypeScript 中的类型注解指定了变量、函数参数和函数返回值的类型。例如,以下代码指定了 `name` 参数的类型为 `string`: ```typescript function greeter(name: string) { return "Hello, " + name + "!"; } ``` ## 接口 接口定义了一个对象的属性和方法。例如,以下代码定义了一个 `Person` 接口: ```typescript interface Person { firstName: string; lastName: string; } function greeter(person: Person) { return "Hello, " + person.firstName + " " + person.lastName + "!"; } let user = { firstName: "John", lastName: "Doe" }; console.log(greeter(user)); ``` ## 类 类是面向对象编程的基本构建块。以下是一个简单的类的示例: ```typescript class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return "Hello, " + this.greeting + "!"; } } let greeter = new Greeter("world"); console.log(greeter.greet()); ``` ## 泛型 泛型允许在编写代码时指定未知的类型。以下是一个泛型函数的示例: ```typescript function identity<T>(arg: T): T { return arg; } let output = identity<string>("hello"); console.log(output); ``` ## 命名空间 命名空间是用于组织和管理代码的方式。以下是一个简单的命名空间的示例: ```typescript namespace MyNamespace { export function sayHello() { console.log("Hello from MyNamespace!"); } } MyNamespace.sayHello(); ``` ## 模块 模块是将代码分离成可重用的单元的方式。以下是一个简单的模块的示例: ```typescript export interface Person { firstName: string; lastName: string; } export class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return "Hello, " + this.greeting + "!"; } } ``` 可以使用 `import` 关键字导入模块中的对象: ```typescript import { Person, Greeter } from "./module"; let user: Person = { firstName: "John", lastName: "Doe" }; let greeter = new Greeter("world"); console.log(greeter.greet()); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值