iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 27
0
自我挑戰組

Typescript 初心者手札系列 第 27

【Day 27】在 React 專案中使用 TypeScript - 命名空間(namespace)

前兩天初步的探討了模組的基本皮毛(每一個模組規範都是個深耕(誤),仍待細細研究)。今天接著要來探討在探討模組時,一直感到很疑惑的命名空間(namespace)。

首先,要特別注意的是 namespace 這個關鍵字。官網有提在 TS 1.5 版本之前 namespace 其實稱為內部模組(Internal modules),語法上會使用 module X {},但為了避免跟 ES module的語法保持一致,將過去的內部模組改為「命名空間」(namespace),語法為 namespace X{},而原本的外部模組(External module)就稱為模組(module)。

那究竟命名空間的用途是什麼?和模組又有什麼差別呢?

命名空間(namespace)

命名空間是 TS 內建的機制,在 JS 中沒有,主要用於將邏輯功能分類,透過封裝具有共同關係的元素和物件,讓開發者可以更簡潔地組織程式碼且避免全域變數可能的命名衝突或可能的污染。命名空間內部可以包含介面、類別、函式和變數等。

命名空間可以跨檔案分割,並使用 -outFile 就可以串聯每個檔案。

1. 宣告方式

命名空間的宣告以關鍵字 namespace 標註,後面接命名空間的名稱,如下:

namespace SomeNameSpaceName { 
   export interface A {      }  
   export class B {      }  
} 

若希望在命名空間外部使用,就要在前面加上 export 關鍵字。這時候,就可以透過語法SomeNameSpaceName.A 和 SomeNameSpaceName.B 來調用命名空間內的介面和類別。

2. 跨檔案分割

我們可以將命名空間分成多個檔案,而這些檔案會有相依性,因此,必須使用參考標記來告知編譯器檔案之間的關係。
語法如下:

/// <reference path="檔案名稱"/>

舉例來說:
假設有一個 IShape.ts 檔案裡面編寫了命名空間 Drawing,命名空間內有一個外部可調用的介面Ishape

Ishape.ts 檔案
namespace Drawing { 
   export interface IShape { 
      draw(); 
   }
}  

這時候有另外幾個檔案會使用 Drawing 命名空間,這時候就必須使用參考標記。在Circle.ts檔案同樣編寫了Drawing 命名空間(別忘了命名空間是可以跨檔案分割的唷),裡面有一個實踐 Ishape 介面的類別 Circle。由於 Ishape 介面宣告在 Ishape.ts 檔案中,需要使用<reference path = "IShape.ts" />才能在Circle.ts檔案中使用。

Circle.ts 檔案
/// <reference path = "IShape.ts" /> 
namespace Drawing { 
   export class Circle implements IShape { 
      public draw() { 
         console.log("Circle is drawn"); 
      }  
    }
}      
Triangle.ts 檔案
/// <reference path = "IShape.ts" /> 
namespace Drawing { 
     export class Triangle implements IShape { 
        public draw() { 
           console.log("Triangle is drawn"); 
        } 
    } 
}
TestShape.ts 檔案

在 TestShape.ts 檔案中與上面三個檔案中的宣告相依,因此需要參考註記三個檔案,但要注意的是,寫註記的順序是會影響的,和<script>標籤引入相同概念。

/// <reference path = "IShape.ts" />   
/// <reference path = "Circle.ts" /> 
/// <reference path = "Triangle.ts" />  
function drawAllShapes(shape:Drawing.IShape) { 
    shape.draw(); 
} 
drawAllShapes(new Drawing.Circle());
drawAllShapes(new Drawing.Triangle());
}

現在命名空間 Drawing 的內部宣告分散在幾個.ts檔案中,如果要編譯輸出成同一隻 JS 檔案要如何做呢? 語法如下:

tsc --outFile sample.js TestShape.ts

我們將最後一隻引入三個分散命名空間 Drawing 的函式執行檔案 TestShape.ts 編譯輸出成 sample.js 檔案。這時候,編譯器會預設根據引用標籤的順序自動地排序編譯。

當然,也可以每個編譯檔案都列出來如下:

 tsc --outFile sample.js Ishape.ts Circle.ts Triangle.ts TestShape.ts
 
 //等於
 
tsc --outFile sample.js TestShape.ts

另一方面,你也可以選擇編譯成多個JS檔案,這時候就需要使用<script>在 html 檔案使用標籤以適當的順序加載每個檔案。

Mypage.html檔案
 <script src="Ishape.js" type="text/javascript" />
 <script src="Circle.js" type="text/javascript" />
 <script src="Triangle.js" type="text/javascript" />
<script src="TestShape.js" type="text/javascript" />

3. 命名空間可以嵌套(Nested)

命名空間內可以宣告命名空間,可以使用.運算符來取得內部命名空間的物件,語法如下:

namespace namespace_name1 { 
   export namespace namespace_name2 {
      export class class_name {    } 
   } 
}

舉個例子來說:

Invoice.ts 檔案
namespace Una { 
   export namespace invoiceApp { 
      export class Invoice { 
         public calculateDiscount(price: number) { 
            return price * .40; 
         } 
      } 
   } 
}
InvoiceTest.ts 檔案
/// <reference path = "Una.ts" />
var invoice = new Una.invoiceApp.Invoice(); 
console.log(invoice.calculateDiscount(500));

上面的範例使用了 Una.invoiceApp 取得了Una命名空間中的 invoiveApp命名空間。

命名空間的主要三個特性暫且探討到這邊,但是,好像還是很不清楚究竟什麼時候會使用namespace?namespace 和 module 的差別又是什麼?

我查找了許多文件,但是對於使用情境仍然有點模糊。(由於這次鐵人賽文章是第一次碰到 TS ,主要以閱讀文件來了解 TS 基礎概念為主,尚未進入實際開發階段,或許之後真正遇到了才有辦法說得更清楚)。這邊只能先整理我所閱讀的文章知識點,但觀念的印證就無法做到了...)

先來整理個結論:

  1. 參考 STEVE FENTON 的文章,命名空間和模組不應該混用,只能選擇其一,且推薦優先使用模組。
  2. 官網 中提到若是Node.js應用以及大型的專案,推薦使用模組。且從ES6開始,模組已是內建機制,幾乎所有引擎都支援,所以新專案推薦使用模組來組織程式碼。

換句話說,目前實際開發上似乎很少使用命名空間,大部分使用模組來組織程式碼結構,事實上真是如此嗎?可能要有實際開發經驗的前輩們來解答了!


上一篇
【Day 26】在 React 專案中使用 TypeScript - 模組(Module)觀念(下)
下一篇
【Day 28】在 React 專案中使用 TypeScript - 宣告檔案(declaration file)
系列文
Typescript 初心者手札30

尚未有邦友留言

立即登入留言