メインコンテンツへ移動

モジュール_TypeScript ノート 13

無料2019-03-30#TypeScript#TypeScript reference elision#ES Module in TypeScript#TypeScript模块化#TypeScript按需加载#TypeScript dynamic require#TypeScript动态加载

兼容并包のモジュールメカニズム

一.構文フォーマット

TypeScript は ES Module 規範と互換性があり、ファイルがモジュールです

簡単に言えば、ファイル中に合法な import または export 文が含まれている場合、モジュールとして扱われます(モジュールスコープを持ち)、否则グローバルスコープ下で実行されます。例えば:

let x = 1
function f() { }
// 会被编译成
var x = 1;
function f() { }

// 而
let x = 1
export function f() { }
// 会被编译成(以 AMD 形式为例)
define(["require", "exports"], function (require, exports) {
  "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  var x = 1;
  function f() { }
  exports.f = f;
});

あらゆる宣言は import/export 可能で、インターフェース、タイプエイリアスなどを含みます:

export interface StringValidator {
  isAcceptable(s: string): boolean;
}
export type PhoneNumber = string;

特殊的、純粋な宣言ファイル(例えば d.ts)は実際的な意味のあるコードを生成しませんが、それでもモジュール(スコープ)隔離を持ちます

// 上例会被编译成
define(["require", "exports"], function (require, exports) {
  "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
});

これも d.ts 分類 の根拠の 1 つです

P.S. import/export 具体構文は ES Module を参照。ここでは展開しません

CommonJS モジュールサポート

[CommonJS と AMD モジュール](/articles/module-es6 笔记 13/#articleHeader2) をサポートするために、TypeScript は特殊な構文を提供します:

export = something;

モジュールのエクスポートオブジェクトを定義するために使用され、NodeJS 内の以下に類似:

// NodeJS モジュール(CommonJS)
let x = {a: 1};
exports.x = x;
module.exports = x;

TypeScript に書き換えるとこのようになります:

let x = {a: 1};
export = x;
// 会被编译成
define(["require", "exports"], function (require, exports) {
  "use strict";
  var x = { a: 1 };
  return x;
});

対応するインポート構文も NodeJS(require('./myModule.js'))とは異なります:

import module = require("myModule")

二.モジュールコード生成

コンパイルオプション(--module または -m)を通じて生成コードのモジュールフォーマットを指定できます:

  // tsc -m xxx
 'commonjs' # NodeJS モジュール定義
 'amd'      # AMD
 'system'   # SystemJS
 'umd'      # UMD
 'es6'      # ES Module
 'es2015'   # es6 と等価
 'esnext'   # まだ ES 規範に収録されていない最先端モジュール定義、例えば `import(), import.meta` など
 'none'     # すべてのモジュール定義を無効化、例えば import, export など(使用するとエラー)

デフォルトモジュールフォーマットは CommonJS または ES6 で、--target オプションに関係します(target === "ES3" or "ES5" ? "CommonJS" : "ES6")。もし将来の新バージョン ES 規範中でモジュール定義に変更がある場合、さらに es2019, es2020... などの値が追加され、ES 規範の各バージョン中のモジュール定義に対応します(モジュール定義に変更がない場合は、追加しません)

P.S. 具体のモジュール生成例は、Code Generation for Modules を参照

--module--target

--target(または -t)オプションは --module と非常に似ており、値は以下の通り:

// tsc -t xxx
'es3'
'es5'
'es2015'
'es2016'
'es2017'
'es2018'
'esnext'

生成される目標コードがどのバージョンの規範で定義された言語特性をサポートするかを表します(デフォルト ES3)。--module オプションとは独立しています:

The module system is independent of the language implementation.

完全に ES6 モジュールフォーマットを満たす ES5 コードをコンパイル生成できるからです。例えば:

// tsconfig.json
"compilerOptions": {
  "target": "es5",
  "module": "es6"
}

さらに、値も --module とは異なり、各バージョンの ES 規範は 1 つの --target 具体値に対応します。各バージョンごとに新しい特性が追加されるためです

P.S. より多くの関連討論は、Understanding "target" and "module" in tsconfig を参照

P.S. 注意、--module--target はすべて生成される目標コードを対象としたもので、ソースコードとは無関係 です(ソースコードは完全に ES 特性全開でも構いません。例えば --libES2016, es2017... を指定)

三.モジュールインポート

一般に、import/require は目標モジュールソースコードをインポートし、そこからタイプ情報を抽出します。例えば:

// myModule.ts
export default {
  name: 'my-module',
  f() {
    console.log('this is my module.');
  }
}

// index.ts
import MyModule from './MyModule';
let m = MyModule;
// m のタイプ为 { name: string; f(): void; }
m.f();

--module commonjs 下で)index.ts のコンパイル結果は:

exports.__esModule = true;
var MyModule_1 = require("./MyModule");
var m = MyModule_1["default"];
// m のタイプ为 { name: string; f(): void; }
m.f();

オンデマンドロード

特殊的、生成される目標コード中でインポートされたモジュールが使用されていない場合(例えばタイプ注釈中でのみ使用)、コンパイル時にモジュール参照が自動的に削除されます:

// index.ts
import MyModule from './MyModule';
let m: typeof MyModule;

// 编译结果
exports.__esModule = true;
var m;

この非必須参照を除去(reference-elision) する特性は、オンデマンドロードのシナリオで特に重要です:

// タイプをインポート
import MyModule from './MyModule';
declare function require(moduleName: string): any;
let someCondition: boolean;
if (someCondition) {
  let m: typeof MyModule = require('./MyModule');
  // 同様に正しいタイプを持ちます
  m.f();
}

// 编译结果
"use strict";
exports.__esModule = true;
var someCondition;
if (someCondition) {
  var m = require('./MyModule');
  // 同様に正しいタイプを持ちます
  m.f();
}

四.モジュールタイプ宣言

タイプが不足しているサードパーティモジュールに対して、宣言ファイル(d.ts) を通じてタイプ宣言を補充できます

具体的には、declare module 'my-module' {} 構文はモジュールを宣言できます(import/require 可能):

// types.d.ts
declare module "my-module" {
  function f(): string;
}

// index.ts
import { f } from "my-module";
const result: string = f();

この方式を通じてサードパーティモジュールのタイプを補填できますが、もし素早く使用したいだけ(手動でタイプを補いたくない)の場合、メンバー宣言を省略できます。そのすべてのメンバーは any タイプになります:

// types.d.ts
declare module "my-module";

// index.ts
import x, {y} from "my-module";
x(y);

ワイルドカード

特殊的、一部のローディングシステムは非 JavaScript コンテンツのインポートをサポートします。例えば AMD

define(['text!../templates/start.html'], function (template) {
    //do something with the template text string.
});

此時モジュールワイルドカードを通じてそのタイプを定義できます:

// text! で始まるすべてのモジュールのタイプを記述
declare module "text!*" {
  const content: string;
  export default content;
}
// !text で終わるすべてのモジュールのタイプを記述
declare module "*!text" {
  const content: string;
  export default content;
}

これらの特殊なモジュールはタイプ情報を持ちます:

import html from 'text!../templates/start.html';
// 正确
html.trim();

UMD モジュール

UMD の特徴は CommonJS と AMD モジュールローディングと互換性があり、グローバルに露出して直接使用もできるため、そのモジュール宣言も比較的特殊です:

// math-lib.d.ts
export function isPrime(x: number): boolean;
export as namespace mathLib;

2 種類の参照方式:

// 直接グローバル変数を通じてアクセス
mathLib.isPrime(12);
// モジュールインポート
import { isPrime } from './math-lib';
isPrime(122);

参考資料

コメント

コメントはまだありません

コメントを書く