TypeScript Generics(泛型)

来源:http://www.cnblogs.com/ys-ys/archive/2016/03/04/5241783.html
-Advertisement-
Play Games

軟體工程的一個主要部分就是構建組件,構建的組件不僅需要具有明確的定義和統一的介面,同時也需要組件可復用。支持現有的數據類型和將來添加的數據類型的組件為大型軟體系統的開發過程提供很好的靈活性。 在C#和Java中,可以使用"泛型"來創建可復用的組件,並且組件可支持多種數據類型。這樣便可以讓用戶根據自己


軟體工程的一個主要部分就是構建組件,構建的組件不僅需要具有明確的定義和統一的介面,同時也需要組件可復用。支持現有的數據類型和將來添加的數據類型的組件為大型軟體系統的開發過程提供很好的靈活性。

在C#和Java中,可以使用"泛型"來創建可復用的組件,並且組件可支持多種數據類型。這樣便可以讓用戶根據自己的數據類型來使用組件。

泛型的簡單案例

首先,用泛型寫一個"Hello World":identity函數。identity函數將會返回我們傳入的數據。你可以認為它是個"echo"命令。
不用泛型,我們也不用給identity函數指定類型:

function identity(arg: number): number {
    return arg;
}

或者,我們可以給identity函數指定"any"類型:

function identity(arg: any): any {
    return arg;
}

雖然使用"any"類型的時候可以接收任何類型的"arg"參數,但是實際上已經失去函數返回值類型的信息。假如我們傳入一個number,我們只知道返回任何類型的值都是可以的。

所以,我們需要一直方式來捕捉參數的類型,也可以用它來表示返回值的類型。這裡使用的是"類型變數",一種特殊的變數,代表的是類型而非值。

function identity<T>(arg: T): T {
    return arg;
}

現在我們已經為identity函數添加了類型變數"T"。"T"允許捕獲用戶提供的參數類型(如:number),以便我們稍後可以使用該類型。然後我們再次用"T"作為返回值的類型。現在我們可以看到,同一類型被用來作為參數類型和返回值類型。

我們稱這個版本的identity函數為泛型,它可用於多種類型。與使用"any"類型不同,它和第一個identity函數(使用number作為參數類型和返回值類型)一樣精準(它不會失去任何信息)。

一旦我們定義了泛型函數,有兩種方法可以使用。第一種就是傳入所有的參數,包括類型參數:

var output = identity<string>("myString"); // output的類型將會是 'string'

在這裡,我們明確的將"T"指定為string,作為函數中傳入的參數,使用<>包裹該參數而非()。

第二種是最常見的。我們使用/類型推斷/,我們希望編譯器根據傳入的參數自動為"T"指定類型。

var output = identity("myString"); // output的類型將會是 'string'

註意,我們並未顯示的給尖括弧<>內傳入類型,編譯器檢查"myString",然後將"T"設置為它的類型。雖然類型推斷是個很實用的工具,也能夠使代碼簡短易讀,但你還是需要跟前面的例子一樣明確的傳遞類型參數,因為可能會存在複雜的函數,使得編譯器未能正確的進行類型推斷。

使用泛型

當你開始使用泛型,你可能會註意到當你創建一個類似"identity"的泛型函數,編譯器會強制要求你在函數中正確的使用這些通用類型參數。也就是說,你真的把這些參數視為可以是任何類型的。

再看看之前的identity函數:

function identity<T>(arg: T): T {
    return arg;
}

想要每次調用的時候在控制台列印出"arg"參數的length。我們可以嘗試這麼寫:

function loggingIdentity<T>(arg: T): T {
    console.log(arg.length);  // 錯誤: T 不存在 .length
    return arg;
}

當我們這麼做的時候,編譯器會拋出一個錯誤提示我們使用"arg"的".length"屬性,但是沒有地方指定過"arg"的".length"屬性。之前我們說類型變數代表了所有類型,所有可能使用這個函數的時候會傳入一個"number"類型的值,而"number"是沒".length"屬性的。
實際上,我們想讓這個函數接受的參數是個"T"類型的數組而非直接"T"。當傳入的是數組,length屬性便是可用的了。我們可以像創建其他數組類型一樣:

function loggingIdentity<T>(arg: T[]): T[] {
    console.log(arg.length);  // 數組中存在 .length,所以沒報錯
    return arg;
}

你可以這樣理解loggingIdentity函數:loggingIdentity泛型函數,參數類型是"T",參數"arg"是個類型為"T"的數組,返回的也是個類型為"T"的數組。如果我們傳入一個都是數字的數組,那麼我們也會得到一個都是數字的數組,因為這時候"T"類型已經綁定為number了。這使得我們可以使用類型變數"T"作為我們使用的類型的一部分,而非全部類型,這也更具靈活性。

我們可以通過這種方式寫個例子:

function loggingIdentity<T>(arg: Array<T>): Array<T> {
    console.log(arg.length);  // 數組中存在 .length,所以沒報錯
    return arg;
}

泛型類型

在前面例子中,我們創建了通用的identity函數,可以使用於不同的類型。現在,我們將探討函數類型及如何創建泛型介面。

泛型函數的類型與非泛型函數一樣,只是最前面放上一個類型參數,類似與聲明函數:

function identity<T>(arg: T): T {
    return arg;
}

var myIdentity: <T>(arg: T)=>T = identity;

我們也可以給類型中的泛型類型參數指定不同的名稱,只要類型變數的數量和其使用方式都能對應的上。

function identity<T>(arg: T): T {
    return arg;
}

var myIdentity: <U>(arg: U)=>U = identity;

我們也可以使用對象字面量的簽名調用來寫泛型類型:

function identity<T>(arg: T): T {
    return arg;
}

var myIdentity: {<T>(arg: T): T} = identity;

下麵開始寫第一個泛型介面。用上個例子中的對象字面量來寫介面:

interface GenericIdentityFn<T> {
    (arg: T): T;
}

function identity<T>(arg: T): T {
    return arg;
}

var myIdentity: GenericIdentityFn<number> = identity;
var num = myIdentity(10); // 正確,因為類型是number
var str = myIdentity("10") // 錯誤,參數類型不是number

註意,我們的例子稍微有些改變。我們把非泛型函數簽名作為泛型類型的一部分,而不是去描述泛型函數。當我們使用GenericIdentityFn,我們還需要指定對應的類型參數(這裡是number),有效的鎖定在底層簽名調用時會用到的類型。理解"何時將類型參數直接放到簽名調用"和"何時將它放到介面上"將有助於描述哪部分類型屬於泛型。

除了泛型介面,我們還可以創建泛型類。請註意,不可能創建泛型枚舉和模塊。

泛型類

泛型類和泛型介面相似。泛型類在類名後面使用尖括弧<>包含泛型類型參數列表。

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

var myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 1;
myGenericNumber.add = function(x, y) { return x + y; };
alert(myGenericNumber.add(myGenericNumber.zeroValue, 1));  // 2

這是對"GenericNumber"類想當直觀的使用,你也可能註意到並未限制只能使用"number"類型。我們可以使用"string"抑或更複雜的對象。

var stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "Hello ";
stringNumeric.add = function(x, y) { return x + y; };

alert(stringNumeric.add(stringNumeric.zeroValue, "World")); // Hello World

和介面一樣,將類型參數放在類之後來告訴我們類的所有屬性都是同一個類型。

正如前面"類"那一節所描述的,一個類由兩部分組成:靜態部分和實例部分。泛型類僅屬於實例部分,所以當我們使用類的時候,靜態成員不能使用類的類型參數。

泛型的限制

如果你還記得之前的例子,有時候你想要寫一個泛型函數來操作一組類型,並且你是知道這些類型具有什麼功能。

在"loggingIdentity"例子中,我們希望能夠訪問"arg"的".length"屬性,但是編譯器不能確定每個類型都有".length"屬性,所以它將報錯提示我們不能這麼做。

function loggingIdentity<T>(arg: T): T {
    console.log(arg.length);  // 錯誤: T 不存在 .length
    return arg;
}

相對於處理任何類型或者所有類型,我們更希望強制去要求函數去處理帶有".length"屬性的任何類型或者所有類型。只要該類型有這個成員(屬性),我們便運行通過,也就是必須包含這個指定的成員(屬性)。

既然需要這麼做,我們就創建一個描述限制的介面。在這裡,先創建一個只有單個屬性".length"的介面,然後使用這個介面和"extends"關鍵字來指明限制:

interface hasLength {
    length: number;
}

function loggingIdentity<T extends hasLength>(arg: T): T {
    console.log(arg.length);  // 現在我們知道它含有.length屬性,並且不報錯
    return arg;
}

因為這個泛型函數現在是有限制的,所以它不在支持任何類型或者所有類型:

loggingIdentity(3); // 錯誤,number不包含.length屬性

因此,我們需要傳入其類型具有所需屬性的值:

loggingIdentity({length: 10, value: 3}); // 正確

在泛型中使用類類型
當在TypeScript中使用泛型創建工廠函數的時候,需要引用其構造函數的類類型。

class Greeter{
    greeter:string = "Hello World";
}
function create<T>(c: {new(): T}): T { 
    return new c();
}
var newGreeter = create<Greeter>(Greeter);

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 針對BFC的特性之一:BFC之間互不影響原則,so,我們可以實現超級無敵的寬度自適應佈局。詳情見隨筆
  • 不廢話直接上代碼: HTML: <a class="js-tel tel" data-tel="1312414"></a> JS: 1 // zepto tel 2 $('body').on('tap', '.js-tel', function() { 3 var _this = $(this);
  • ---恢復內容開始--- JavaScript 計時器在JavaScript中,我們可以在設定的時間間隔之後來執行代碼,而不是在函數被調用後立即執行。計時器類型:一次性計時器:僅在指定的延遲時間之後觸發一次。間隔性觸發計時器:每隔一定的時間間隔就觸發一次。計時器方法: 計時器setInterval(
  • 最詳細的JavaScript和事件解讀 與瀏覽器進行交互的時候瀏覽器就會觸發各種事件。比如當我們打開某一個網頁的時候,瀏覽器載入完成了這個網頁,就會觸發一個 load 事件;當我們點擊頁面中的某一個“地方”,瀏覽器就會在那個“地方”觸發一個 click 事件。 這樣,我們就可以編寫 JavaScri
  • Selectors (選擇器) ---------------參考MDN中css學習。 首先css選擇器有很多,但總體概括起來的話有兩種: 標簽選擇器(*是特殊情況),可但標簽,也可上下文多標簽; 屬性選擇器(id和class都是屬性,特殊的屬性); 標簽選擇器~單標簽 指此單個的標簽名字,使用時可
  • 網站開發人員經常需要檢查網站的相容性,在各種瀏覽器中,以確保網站的作品完美地在所有的瀏覽器。為此,有大量的跨瀏覽器測試工具,可以幫助開發人員檢查他們的網站之前,他們的網站是。 這裡是全集合的一些最好的免費以及高級跨瀏覽器測試工具,將允許開發人員測試一切。我們希望你會喜歡這個集合,併發現這些工具對你有
  • 1、HTML5 簡介 HTML5 是最新的 HTML 標準,他是萬維網的核心語言、標準通用標記語言下的一個應用“超文本標記語言”。 HTML 的上一個標準 HTML4.01 誕生於 1999年,他的第一代標準誕生於 1995年,自此 Web 世界經歷了巨變,在 HTML4.01 中提出了網頁結構與表
  • offsetLeft 獲取的是相對於父對象的左邊距 left 獲取或設置相對於 具有定位屬性(position定義為relative)的父對象 的左邊距 如果父div的position定義為relative,子div的position定義為absolute,那麼子div的style.left的值是相
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...