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
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...