TypeScript Modules(2)

Sike發表於2019-02-16

typescript之旅

1.TypeScript-Basic
2.TypeScript interface
3.Typescript-module(1)
4.TypeScript Modules(2)
5.Typescript tsconfig
6.TypeScript Functions
7.Typescript Class

TypeScript-Modules(2).md

Module

CMD和AMD外部依賴模組
如果你並沒有使用node.js或require.js,那就沒有必要使用module關鍵字了,只需要使用namespace

  • namespace中使用reference,module使用
    import someMod = require(`someModule`);

  • export功能不變,使module內部宣告外部可見

  • 編譯module,必須指定–module引數

tsc --module commonjs Test.ts
tsc --module amd Test.ts
  • 編譯器根據import語句,順序匯入依賴檔案

Validation.ts

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

LettersOnlyValidator.ts

import validation = require(`./Validation`);
var lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements validation.StringValidator {
    isAcceptable(s: string) {
        return lettersRegexp.test(s);
    }
}

ZipCodeValidator.ts

import validation = require(`./Validation`);
var numberRegexp = /^[0-9]+$/;
export class ZipCodeValidator implements validation.StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}

Test.ts

import validation = require(`./Validation`);
import zip = require(`./ZipCodeValidator`);
import letters = require(`./LettersOnlyValidator`);

// Some samples to try
var strings = [`Hello`, `98052`, `101`];
// Validators to use
var validators: { [s: string]: validation.StringValidator; } = {};
validators[`ZIP code`] = new zip.ZipCodeValidator();
validators[`Letters only`] = new letters.LettersOnlyValidator();
// Show whether each string passed each validator
strings.forEach(s => {
    for (var name in validators) {
        console.log(`"` + s + `" ` + (validators[name].isAcceptable(s) ? ` matches ` : ` does not match `) + name);
    }
});

上述語法typescript編譯器是如何生成程式碼的?

demo:

SimpleModule.ts

import m = require(`mod`);
export var t = m.something + 1;

AMD / RequireJS SimpleModule.js:

define(["require", "exports", `mod`], function(require, exports, m) {
    exports.t = m.something + 1;
});

CommonJS / Node SimpleModule.js:

var m = require(`mod`);
exports.t = m.something + 1;

簡化上面的例子

export= 語法

  • 在LettersOnlyValidator.ts中有下面的程式碼

import validation = require(`./Validation`);
...
validation. StringValidator.doSometing();

同時我們發現上面例子的兩種validator,每個module都只匯出一個class宣告。如果使用上面的方式匯出,其他檔案import之後,每次都使用validation. StringValidator顯得很沉重

Validation.ts

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

LettersOnlyValidator.ts

import validation = require(`./Validation`);
var lettersRegexp = /^[A-Za-z]+$/;
class LettersOnlyValidator implements validation.StringValidator {
    isAcceptable(s: string) {
        return lettersRegexp.test(s);
    }
}
export = LettersOnlyValidator;

ZipCodeValidator.ts

import validation = require(`./Validation`);
var numberRegexp = /^[0-9]+$/;
class ZipCodeValidator implements validation.StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
export = ZipCodeValidator;

Test.ts(此檔案是重點,zipValidator和lettersValidator可以直接使用)

import validation = require(`./Validation`);
import zipValidator = require(`./ZipCodeValidator`);
import lettersValidator = require(`./LettersOnlyValidator`);

// Some samples to try
var strings = [`Hello`, `98052`, `101`];
// Validators to use
var validators: { [s: string]: validation.StringValidator; } = {};
validators[`ZIP code`] = new zipValidator();
validators[`Letters only`] = new lettersValidator();
// Show whether each string passed each validator
strings.forEach(s => {
    for (var name in validators) {
        console.log(`"` + s + `" ` + (validators[name].isAcceptable(s) ? ` matches ` : ` does not match `) + name);
    }
});

注意
這種簡化寫法只使用於只匯出一個宣告的模組

別名

  • 另一種縮短物件引用名稱的方式。

注意
別名的語法也是很奇葩的,如果名稱太長,可以使用import q = x.y.z ,這個語法和import x = require(`name`) 沒有一毛錢關係,typescript專案組的朋友們,你們加個關鍵字能死嗎?這裡的語法只是為指定的符號建立一個別名。

module Shapes {
    export module Polygons {
        export class Triangle { }
        export class Square { }
    }
}

import polygons = Shapes.Polygons;
var sq = new polygons.Square(); // Same as `new Shapes.Polygons.Square()`

高階模組載入方式

有時候,你緊緊想在滿足某些條件下,才載入某一模組

這個功能確實很贊,但是還是語法的問題,很讓你困惑。

再說之前呢,有必要對這個語法進行深入瞭解

import zipValidator = require(`./ZipCodeValidator`);

按照上面的理解,大多數人肯定認為,這個語法就是nodejs中的
var zipValidator = require(`./ZipCodeValidator`);

這樣理解也沒錯,但是這裡有一個typescript的惰性載入問題。首先我們知道node載入外部模組是很浪費時間的,所以呢,typescript遇到這句話,並不會馬上進行require呼叫,而是等到後面真的要用的該模組時,才會require.看例子:

Test.ts

import StringValidator = require(`./Validation`);
import zipValidator = require(`./ZipCodeValidator`);
import lettersValidator = require(`./LettersOnlyValidator`);

// Some samples to try
var strings = [`Hello`, `98052`, `101`];
// Validators to use
var validators: { [s: string]: StringValidator; } = {};
validators[`ZIP code`] = new zipValidator();

裡面的lettersValidator雖然匯入了,但是後面並沒有使用。
生成的js程式碼如下:

var zipValidator = require(`./ZipCodeValidator`);
// Some samples to try
var strings = [`Hello`, `98052`, `101`];
// Validators to use
var validators = {};
validators[`ZIP code`] = new zipValidator();

ok,這就是我們要了解的背景,typescript的惰性載入問題

利用上面的這個特性,我們實現動態載入(nodejs和requirejs的區別)

Dynamic Module Loading in node.js

declare var require;
import Zip = require(`./ZipCodeValidator`);
if (needZipValidation) {
    var x: typeof Zip = require(`./ZipCodeValidator`);
    if (x.isAcceptable(`.....`)) { /* ... */ }
}

Sample: Dynamic Module Loading in require.js

declare var require;
import Zip = require(`./ZipCodeValidator`);
if (needZipValidation) {
    require([`./ZipCodeValidator`], (x: typeof Zip) => {
        if (x.isAcceptable(`...`)) { /* ... */ }
    });
}
  • 雖然我前面鋪墊了很多,你看到這裡,還是可能看不懂,好吧,我承認我自己在這裡也糾結了好久

declare var require是表示宣告nodejs中的require關鍵詞,否則這裡var x: typeof Zip = require(`./ZipCodeValidator`);,會報錯(require未宣告)。

  • 請注意這裡的require是node中的require,而import Zip = require(`./ZipCodeValidator`);中的require是typescript的關鍵詞,而且只能和import一起用。瞭解這點非常重要!!!

  • import Zip = require(`./ZipCodeValidator`);根據惰性載入機制,這句話什麼都不會做!!!

  • typeof Zip什麼意思,請回頭看高階技巧

生成的js程式碼

if (needZipValidation) {
    var x = require(`./ZipCodeValidator`);
    if (x.isAcceptable(`.....`)) { }
}

使用第三方庫

第三方庫都不是typescript開發的,我們要使用的時候怎麼使用呢?

第三方庫api都是沒有型別資訊的,但是typescript編譯器主要的工作就是檢查型別是否匹配。我們能不能把它們的api全部宣告一遍,這樣我們的程式碼呼叫api的時候,編譯器去檢查宣告型別是否一致,來提示錯誤。
所以要寫.d.ts檔案,比如D3,那就要寫D3.d.ts

  • namespace
    D3.d.ts (simplified excerpt)

declare namespace D3 {
    export interface Selectors {
        select: {
            (selector: string): Selection;
            (element: EventTarget): Selection;
        };
    }

    export interface Event {
        x: number;
        y: number;
    }

    export interface Base extends Selectors {
        event: Event;
    }
}

declare var d3: D3.Base;
  • module

node.d.ts (simplified excerpt)

declare module "url" {
    export interface Url {
        protocol?: string;
        hostname?: string;
        pathname?: string;
    }

    export function parse(urlStr: string, parseQueryString?, slashesDenoteHost?): Url;
}

declare module "path" {
    export function normalize(p: string): string;
    export function join(...paths: any[]): string;
    export var sep: string;
}

Now we can /// <reference> node.d.ts and then load the modules using e.g. import url = require(`url`);.

///<reference path="node.d.ts"/>
import url = require("url");
var myUrl = url.parse("http://www.typescriptlang.org");

相關文章