Programming TypeScript ch10 Namespaces.Modules
- モジュールについて議論するにあたり、次のことを区別することが肝要
- (a) tscによるモジュール解決
- (b) ビルドシステムによるモジュール解決
- webpack
- gulp
- etc.
- (c) 実際にどのようにしてアプリケーションにロードされ実行されるか
<script>
- SystemJS
- etc.
- webpackなんかはこの3つを良しなにやってくれる
- 本章では(a)について学ぶ
A Brief History of JavaScript Modules
- 歴史
- JS誕生 (1995)
- モジュールシステムのたぐい無し
- IIFE: Immediately Invoked Function Expression で名前汚染を防ぎつつ、モジュール横断的に必要な機能は
window
に代入されいた- explicitなAPIではない
- 一度にすべて読み込むと遅いので、動的にスクリプトを読み込むようになってきた
- NodeJS (2009)
- モジュールシステム搭載
- CommonJS
require
/module.exports
- AMDモジュール標準 (2008)
- DojoとRequireJSによる後押し
define()
- Browserify (2011)
- フロントエンドでCommonJS相当のことができるようになった
import
/export
シンタックス
- JS誕生 (1995)
- CommonJSの問題
- requireは同期呼び出しであり、ブラウザでの実行に適さない
- requireの引数は任意の式を受け取ることができ、必ずしも静的に解析できない
- ので、プログラムを実行するまで、参照先ファイルが本当に存在するかわからない
- この問題を解消するために
import
/export
シンタックスが生まれた - 各処理系向けにコンパイルする必要がある
- NodeJS: CommonJS
- ブラウザ: globals等、何らかの読み込める形
import, export
- 基本は
import
/export
を使うべき
a.ts
export function foo() {} export function bar() {}
b.ts
import {foo, bar} from './a' foo() export let result = bar()
- default export
c.ts
export default function meow(loudness: number) {}
d.ts
import meow from './c' meow(11)
- ぜんぶ
e.ts
import * as a from './a' a.foo() a.bar()
- re-export
f.ts
export * from './a' export {result} from './b' // default exportのre-export。書籍サンプルには書いてあるが動かない export meow from './c'
- Companion Object Pattern
g.ts
export let X = 3 // value export type X = {y: string} // type
import {X} from './g' // import value and type let a = X + 1 let b: X = {y: 'z'}
Dynamic Imports
- 初期レンダリング性能を上げる
(async () => { const a = await import('./a') a.foo() a.bar() })()
Using CommonJS and AMD Code
import
/export
そのまま使える
Module Mode Versus Script Mode
- ヒューリスティック
import
/export
があるのはmodule mode- ないのはscript mode
- 大抵module mode
- module modeは、ファイル間でコードを参照するのに
import
/export
必要 - script modeは、トップレベルの変数はすべて他のファイルから見える
import
不要
- script modeのユースケース
- プロトタイプをさくっと作りたい
- 型定義ファイル
Namespaces
- カプセル化のもうひとつの方法
- ただし、moduleのほうを優先すべき
Util.ts
namespace Util { export function foo() {} export namespace Nested { export function bar() {} } }
namespace App { Util.foo() Util.Nested.bar() }
- import不要
- ひいては、ファイルシステム上のファイル名を意識する必要なし
Collisions
- 重複不可
Util.ts
namespace Util { export function foo() {} // Error: Duplicate function implementation. }
Util2.ts
namespace Util { export function foo() {} // Error: Duplicate function implementation. }
App.ts
namespace App { Util.foo() }
Compiles Output
"use strict"; var Util; (function (Util) { function foo() { } Util.foo = foo; })(Util || (Util = {})); //# sourceMappingURL=Util.js.map
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function foo() { } exports.foo = foo; function bar() { } exports.bar = bar; //# sourceMappingURL=a.js.map
Column: Prefer Modules over Namespaces When Possible
- 依存をexplicitに表現できるmoduleのほうが良い、という話
Declaration Merging
- namespaceはvalue以外のあらゆるものとmerge可能
Exercises
enumにstatic method追加
enum MyEnum { FOO = 'foo', BAR = 'bar' } namespace MyEnum { export function hoge(): void {} } const foo = MyEnum.FOO MyEnum.hoge()
- enumとnamespaceをmerge
英語
- nitty-gritty
- 本質
- scam
- 詐欺