Programming TypeScript ch10 Namespaces.Modules

TypeScript勉強メモ

出典: 


  • モジュールについて議論するにあたり、次のことを区別することが肝要

    • (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ではない
    • 一度にすべて読み込むと遅いので、動的にスクリプトを読み込むようになってきた
    • モジュールローダ

      • Dojo (2004)
      • YUI (2005)
      • LABjs (2009)
    • モジュールが満たすべき条件

      • カプセル化されていること
      • 依存がexplicitであること
      • さもないと、どの順番で読み込めばよいかわからない
      • 各モジュールはアプリケーション中で一意の識別子を有すること
    • NodeJS (2009)
    • モジュールシステム搭載
    • CommonJS

      • require / module.exports
    • AMDモジュール標準 (2008)
    • DojoとRequireJSによる後押し
    • define()
    • Browserify (2011)
    • フロントエンドでCommonJS相当のことができるようになった
    • import/exportシンタックス
  • 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
  • tsconfig.jsonのmoduleの設定いかん問わず、IIFE + globalsにコンパイルされる
  • cf. import/exportを使ったa.tsのコンパイル結果
"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

    • 詐欺