iT邦幫忙

2023 iThome 鐵人賽

DAY 27
0
Modern Web

TypeScript 啟動!系列 第 27

[Day 27] TypeScript 模組系統

  • 分享至 

  • xImage
  •  

在很早期 TypeScript 還沒建立出 impotexport 的時候,namespace 是一種組織代碼的方式。但隨著 ES6 模組系統的普及,其**namespace** 的使用上有所下降。當然~命名空間在某些情境下仍然展現出其不可或缺的價值。那就先來一個一個介紹吧~

在撰寫小專案的過程中,很常會看到要 impotexport ,那這些是什麼東西呢?

ES6 import 和 export 模組語法與範例

ES6 (ES2015) 帶來了先進的模組系統(幾乎可以說經典的版本),其中就是可以開始使用 importexport 語法。此模組系統讓 JavaScript 開發者可以直接在瀏覽器和 Node.js 中使用模組,不需要第三方庫,如 CommonJS 或 AMD了。

  • 基本語法:

    • Export: 要讓模組中的某個變數、函式或類別可以被其他模組使用,必須將它導出。

      // myModule.ts
      export const myVar = 10;
      export function myFunction() {...}
      
    • Import: 使用其他模組的導出元素時,需要使用 import 來導入它。

      // anotherModule.ts
      import { myVar, myFunction } from './myModule';
      
    • **預設導出:**每個模組可以有一個預設導出。預設導出的元素可以使用任何名稱來導入。不過要注意的是,只能有一個預設導出,不能有兩個唷。

    // math.ts
    export default function add(x, y) {
        return x + y;
    }
    // app.ts
    import customName from './math';
    

命名空間(namespace

如文章一開始所說,在 TypeScript 的早期版本中,namespace 是一種組織代碼的方式。但隨著 ES6 模組系統的普及,其使用頻率有所下降。當然,命名空間在某些情境下仍然展現出其不可或缺的價值。命名空間不僅可以避免命名衝突,而且還提供了更有組織的程式碼結構,幫助我們將相似或相關的功能集中管理。它也為我們提供了一種封裝的方式,使得外部程式碼難以直接訪問命名空間內的成員。

1. 套件的衝突

當在一個大型專案中,使用多個套件是很正常的,不同套件中可能存在相同的類、函式或變數名稱,這會導致命名衝突。使用命名空間,可以將這些相關的功能放在不同的空間領域下,減少命名衝突的可能性。

namespace FirstPackage {
    export class Demo {
        // ...
    }
}

namespace SecondPackage {
    export class Demo {
        // ...
    }
}

3. 命名空間的融合

TypeScript 支持將多個命名空間的宣告融合成同一個命名空間。這是一個非常強大的功能,允許開發者分散定義命名空間,但在編譯時它們會被合併為一個。(但是我怎麼想都覺得很神秘就是了XD,畢竟寫在不同的地方,希望哪天不會出事。)

namespace MergedNamespace {
    export function func1() {
        // ...
    }
}

namespace MergedNamespace {
    export function func2() {
        // ...
    }
}

// 使用時,MergedNamespace 將包含 func1 和 func2

4. 巢狀命名空間

在命名空間內部,我們還可以定義其他命名空間,這提供了更多的組織代碼的選項。

namespace ParentNamespace {
    export namespace ChildNamespace {
        export function nestedFunc() {
            // ...
        }
    }
}

// 使用時需透過完整的命名空間路徑
ParentNamespace.ChildNamespace.nestedFunc();

總結而言,儘管 ES6 的模組系統在現代開發中廣受歡迎,但命名空間在某些情境下仍然很有用,特別是在需要額外的封裝或組織複雜程式碼的時候。

型別宣告 (Type Declaration)

TypeScript 提供的型別檢查系統非常的棒,但實際上許多現有的 JavaScript 套件和框架尚未遷移到 TypeScript(但現在2023年了,基本上應該沒有什麼還沒遷移)。那時,TypeScript 提供了一個方便的橋梁,可以在 TypeScript 中利用這些 JavaScript 庫,而這方法就是型別宣告 (Type Declaration)。(還真別說,我真的非常好奇為啥這樣取名)

1. 引入純 JavaScript 檔案

當你在 TypeScript 專案中直接引入一個 JavaScript 的函式庫或模組時,TypeScript 編譯器會報錯,因為它不知道這個模組的型別定義。例如:

import * from 'test'; // 純 JavaScript 檔案

如果 test.js 沒有型別定義,TypeScript 就會報錯。

2. 型別定義檔 (.d.ts)

為了解決這個問題,TypeScript 引入了 .d.ts 檔案,也稱為型別定義檔。這些檔案不包含具體的實作,而只描述了 JavaScript 模組的 API 的型別宣告。

例如,對於前面的 test 套件,可以看看有沒有機會使用 npm 來安裝它的型別定義:

npm install --save-dev @types/test

一旦安裝了型別定義,就可以很爽的直接 TypeScript 中使用 test,而 TypeScript 也能為提供正確的型別檢查和 IntelliSense。

2. 手動建立型別定義

在大部分情況下,可能沒有型別定義檔。這時,可以自己為 JavaScript 模組建立一個。假設我有以下 JavaScript 模組:

// myModule.js
module.exports = {
    greet: function(name) {
        return 'Hello, ' + name;
    }
};

就可以為其創建以下型別定義檔:

// myModule.d.ts
declare module 'myModule' {
    export function greet(name: string): string;
}

這樣,在 TypeScript 中引入 myModule 時,它就知道如何處理 greet 函數的型別了。

總結:型別宣告提供了一種方法,允許在 TypeScript 中安全地使用現有的 JavaScript 套件,不需要重寫它們。這大大提高了 TypeScript 的可用性和實用性,並促使更多的開發者遷移。

引入純 JavaScript 套件

當 TypeScript 專案中使用 JavaScript 套件,這通常意味著該套件可能不包含 TypeScript 的型別資訊。以下是在 TypeScript 中處理 JavaScript 套件的基本流程:

1. 尋找已存在的型別定義

在許多情況下,第三方的 TypeScript 開發者可能已經為這些 JavaScript 套件提供了型別定義檔。這些檔案通常都在 DefinitelyTyped 專案中,並且可以透過 npm 安裝。例如,如果想要在 TypeScript 專案中使用 jQuery,可以這麼做:

npm install --save jquery
npm install --save-dev @types/jquery

這樣,就完成了為 jQuery 安裝了型別定義。

2. 手動建立型別定義

如果找不到已存在的型別定義,那麼可能需要自己手動為這些模組建立型別定義。這是一個稍微複雜的過程,但只要了解基本的 TypeScript 語法,這也不算太難。直接開始創建一個 .d.ts 檔案來宣告模組的型別,並在我的 TypeScript 設定中引用它。

3. 使用 any 逃避型別檢查(大絕招)

如果只是想快速使用一個套件而不考慮型別,可以直接使用 any 類型來告訴 TypeScript 忽略型別檢查。但請務必注意,將直接失去使用 TypeScript 的優點。

declare module 'some-js-library' {
  var exports: any;
  export = exports;
}

當然,這絕不是最佳方法,因為已經失去 TypeScript 帶來的型別安全性。


上一篇
[Day 26] TypeScript 零售業者模擬演練 IV
下一篇
[Day 28] TypeScript ECMAScript
系列文
TypeScript 啟動!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言