在线不卡日本ⅴ一区v二区_精品一区二区中文字幕_天堂v在线视频_亚洲五月天婷婷中文网站

  • <menu id="lky3g"></menu>
  • <style id="lky3g"></style>
    <pre id="lky3g"><tt id="lky3g"></tt></pre>

    TypeScript 泛型(generic) 詳解

    介紹

    軟件工程中,我們不僅要?jiǎng)?chuàng)建一致的定義良好的API,同時(shí)也要考慮可重用性。 組件不僅能夠支持當(dāng)前的數(shù)據(jù)類型,同時(shí)也能支持未來的數(shù)據(jù)類型,這在創(chuàng)建大型系統(tǒng)時(shí)為你提供了十分靈活的功能。

    在像C#和Java這樣的語(yǔ)言中,可以使用泛型來創(chuàng)建可重用的組件,一個(gè)組件可以支持多種類型的數(shù)據(jù)。 這樣用戶就可以以自己的數(shù)據(jù)類型來使用組件。

    泛型之Hello World

    下面來創(chuàng)建第一個(gè)使用泛型的例子:identity函數(shù)。 這個(gè)函數(shù)會(huì)返回任何傳入它的值。 你可以把這個(gè)函數(shù)當(dāng)成是echo命令。

    不用泛型的話,這個(gè)函數(shù)可能是下面這樣的:

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

    或者,我們使用any類型來定義函數(shù):

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

    使用any類型會(huì)導(dǎo)致這個(gè)函數(shù)可以接收任何類型的arg參數(shù),這樣就丟失了一些信息:傳入的類型與返回的類型應(yīng)該是相同的。 如果我們傳入一個(gè)數(shù)字,我們只知道任何類型的值都有可能被返回。

    因此,我們需要一種方法使返回值的類型與傳入?yún)?shù)的類型是相同的。 這里,我們使用了類型變量,它是一種特殊的變量,只用于表示類型而不是值。

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

    我們給identity添加了類型變量T。 T幫助我們捕獲用戶傳入的類型(比如:number),之后我們就可以使用這個(gè)類型。 之后我們?cè)俅问褂昧薚當(dāng)做返回值類型?,F(xiàn)在我們可以知道參數(shù)類型與返回值類型是相同的了。 這允許我們跟蹤函數(shù)里使用的類型的信息。

    我們把這個(gè)版本的identity函數(shù)叫做泛型,因?yàn)樗梢赃m用于多個(gè)類型。 不同于使用any,它不會(huì)丟失信息,像第一個(gè)例子那樣保持準(zhǔn)確性,傳入數(shù)值類型并返回?cái)?shù)值類型。

    我們定義了泛型函數(shù)后,可以用兩種方法使用。 第一種是,傳入所有的參數(shù),包含類型參數(shù):

    let output = identity(“myString”); // type of output will be ‘string’

    這里我們明確的指定了T是string類型,并做為一個(gè)參數(shù)傳給函數(shù),使用了括起來而不是()。

    第二種方法更普遍。利用了類型推論 – 即編譯器會(huì)根據(jù)傳入的參數(shù)自動(dòng)地幫助我們確定T的類型:

    let output = identity(“myString”); // type of output will be ‘string’

    注意我們沒必要使用尖括號(hào)()來明確地傳入類型;編譯器可以查看myString的值,然后把T設(shè)置為它的類型。 類型推論幫助我們保持代碼精簡(jiǎn)和高可讀性。如果編譯器不能夠自動(dòng)地推斷出類型的話,只能像上面那樣明確的傳入T的類型,在一些復(fù)雜的情況下,這是可能出現(xiàn)的。

    使用泛型變量

    使用泛型創(chuàng)建像identity這樣的泛型函數(shù)時(shí),編譯器要求你在函數(shù)體必須正確地使用這個(gè)通用的類型。 換句話說,你必須把這些參數(shù)當(dāng)做是任意或所有類型的。

    看下之前identity例子:

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

    如果我們想同時(shí)打印出arg的長(zhǎng)度。 我們很可能會(huì)這樣做:

    function loggingIdentity(arg: T): T { console.log(arg.length); // Error: T doesn’t have .length return arg;}

    如果這么做,編譯器會(huì)報(bào)錯(cuò)說我們使用了arg的.length屬性,但是沒有地方指明arg具有這個(gè)屬性。 記住,這些類型變量代表的是任意類型,所以使用這個(gè)函數(shù)的人可能傳入的是個(gè)數(shù)字,而數(shù)字是沒有.length屬性的。

    現(xiàn)在假設(shè)我們想操作T類型的數(shù)組而不直接是T。由于我們操作的是數(shù)組,所以.length屬性是應(yīng)該存在的。 我們可以像創(chuàng)建其它數(shù)組一樣創(chuàng)建這個(gè)數(shù)組:

    function loggingIdentity(arg: T[]): T[] { console.log(arg.length); // Array has a .length, so no more error return arg;}

    你可以這樣理解loggingIdentity的類型:泛型函數(shù)loggingIdentity,接收類型參數(shù)T和參數(shù)arg,它是個(gè)元素類型是T的數(shù)組,并返回元素類型是T的數(shù)組。 如果我們傳入數(shù)字?jǐn)?shù)組,將返回一個(gè)數(shù)字?jǐn)?shù)組,因?yàn)榇藭r(shí)T的的類型為number。 這可以讓我們把泛型變量T當(dāng)做類型的一部分使用,而不是整個(gè)類型,增加了靈活性。

    我們也可以這樣實(shí)現(xiàn)上面的例子:

    function loggingIdentity(arg: Array): Array { console.log(arg.length); // Array has a .length, so no more error return arg;}

    使用過其它語(yǔ)言的話,你可能對(duì)這種語(yǔ)法已經(jīng)很熟悉了。 在下一節(jié),會(huì)介紹如何創(chuàng)建自定義泛型像Array一樣

    泛型類型

    上一節(jié),我們創(chuàng)建了identity通用函數(shù),可以適用于不同的類型。 在這節(jié),我們研究一下函數(shù)本身的類型,以及如何創(chuàng)建泛型接口。

    泛型函數(shù)的類型與非泛型函數(shù)的類型沒什么不同,只是有一個(gè)類型參數(shù)在最前面,像函數(shù)聲明一樣:

    function identity(arg: T): T { return arg;}let myIdentity: (arg: T) => T = identity;

    我們也可以使用不同的泛型參數(shù)名,只要在數(shù)量上和使用方式上能對(duì)應(yīng)上就可以。

    function identity(arg: T): T { return arg;}let myIdentity: (arg: U) => U = identity;

    我們還可以使用帶有調(diào)用簽名的對(duì)象字面量來定義泛型函數(shù):

    function identity(arg: T): T { return arg;}let myIdentity: {(arg: T): T} = identity;

    這引導(dǎo)我們?nèi)懙谝粋€(gè)泛型接口了。 我們把上面例子里的對(duì)象字面量拿出來做為一個(gè)接口:

    interface GenericIdentityFn { (arg: T): T;}function identity(arg: T): T { return arg;}let myIdentity: GenericIdentityFn = identity;

    一個(gè)相似的例子,我們可能想把泛型參數(shù)當(dāng)作整個(gè)接口的一個(gè)參數(shù)。 這樣我們就能清楚的知道使用的具體是哪個(gè)泛型類型(比如:Dictionary而不只是Dictionary)。 這樣接口里的其它成員也能知道這個(gè)參數(shù)的類型了。

    interface GenericIdentityFn { (arg: T): T;}function identity(arg: T): T { return arg;}let myIdentity: GenericIdentityFn = identity;

    注意,我們的示例做了少許改動(dòng)。 不再描述泛型函數(shù),而是把非泛型函數(shù)簽名作為泛型類型一部分。 當(dāng)我們使用GenericIdentityFn的時(shí)候,還得傳入一個(gè)類型參數(shù)來指定泛型類型(這里是:number),鎖定了之后代碼里使用的類型。 對(duì)于描述哪部分類型屬于泛型部分來說,理解何時(shí)把參數(shù)放在調(diào)用簽名里和何時(shí)放在接口上是很有幫助的。

    除了泛型接口,我們還可以創(chuàng)建泛型類。 注意,無(wú)法創(chuàng)建泛型枚舉和泛型命名空間。

    泛型類

    泛型類看上去與泛型接口差不多。 泛型類使用()括起泛型類型,跟在類名后面。

    class GenericNumber { zeroValue: T; add: (x: T, y: T) => T;}let myGenericNumber = new GenericNumber();myGenericNumber.zeroValue = 0;myGenericNumber.add = function(x, y) { return x + y; };

    GenericNumber類的使用是十分直觀的,并且你可能已經(jīng)注意到了,沒有什么去限制它只能使用number類型。 也可以使用字符串或其它更復(fù)雜的類型。

    let stringNumeric = new GenericNumber();stringNumeric.zeroValue = “”;stringNumeric.add = function(x, y) { return x + y; };alert(stringNumeric.add(stringNumeric.zeroValue, “test”));

    與接口一樣,直接把泛型類型放在類后面,可以幫助我們確認(rèn)類的所有屬性都在使用相同的類型。

    我們?cè)陬惸枪?jié)說過,類有兩部分:靜態(tài)部分和實(shí)例部分。 泛型類指的是實(shí)例部分的類型,所以類的靜態(tài)屬性不能使用這個(gè)泛型類型。

    泛型約束

    你應(yīng)該會(huì)記得之前的一個(gè)例子,我們有時(shí)候想操作某類型的一組值,并且我們知道這組值具有什么樣的屬性。 在loggingIdentity例子中,我們想訪問arg的length屬性,但是編譯器并不能證明每種類型都有l(wèi)ength屬性,所以就報(bào)錯(cuò)了。

    function loggingIdentity(arg: T): T { console.log(arg.length); // Error: T doesn’t have .length return arg;}

    相比于操作any所有類型,我們想要限制函數(shù)去處理任意帶有.length屬性的所有類型。 只要傳入的類型有這個(gè)屬性,我們就允許,就是說至少包含這一屬性。 為此,我們需要列出對(duì)于T的約束要求。

    為此,我們定義一個(gè)接口來描述約束條件。 創(chuàng)建一個(gè)包含.length屬性的接口,使用這個(gè)接口和extends關(guān)鍵字還實(shí)現(xiàn)約束:

    interface Lengthwise { length: number;}function loggingIdentity(arg: T): T { console.log(arg.length); // Now we know it has a .length property, so no more error return arg;}

    現(xiàn)在這個(gè)泛型函數(shù)被定義了約束,因此它不再是適用于任意類型:

    loggingIdentity(3); // Error, number doesn’t have a .length property

    我們需要傳入符合約束類型的值,必須包含必須的屬性:

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

    在泛型約束中使用類型參數(shù)

    你可以聲明一個(gè)類型參數(shù),且它被另一個(gè)類型參數(shù)所約束。 比如,現(xiàn)在我們想要用屬性名從對(duì)象里獲取這個(gè)屬性。 并且我們想要確保這個(gè)屬性存在于對(duì)象obj上,因此我們需要在這兩個(gè)類型之間使用約束。

    function getProperty(obj: T, key: K) { return obj[key];}let x = { a: 1, b: 2, c: 3, d: 4 };getProperty(x, “a”); // okaygetProperty(x, “m”); // error: Argument of type ‘m’ isn’t assignable to ‘a’ | ‘b’ | ‘c’ | ‘d’.

    在泛型里使用類類型

    在TypeScript使用泛型創(chuàng)建工廠函數(shù)時(shí),需要引用構(gòu)造函數(shù)的類類型。比如,

    function create(c: {new(): T; }): T { return new c();}

    一個(gè)更高級(jí)的例子,使用原型屬性推斷并約束構(gòu)造函數(shù)與類實(shí)例的關(guān)系。

    class BeeKeeper { hasMask: boolean;}class ZooKeeper { nametag: string;}class Animal { numLegs: number;}class Bee extends Animal { keeper: BeeKeeper;}class Lion extends Animal { keeper: ZooKeeper;}function createInstance(c: new () => A): A { return new c();}createInstance(Lion).keeper.nametag; // typechecks!createInstance(Bee).keeper.hasMask; // typechecks!

    鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場(chǎng),版權(quán)歸原作者所有,如有侵權(quán)請(qǐng)聯(lián)系管理員(admin#wlmqw.com)刪除。
    上一篇 2022年6月15日 06:10
    下一篇 2022年6月15日 06:11

    相關(guān)推薦

    聯(lián)系我們

    聯(lián)系郵箱:admin#wlmqw.com
    工作時(shí)間:周一至周五,10:30-18:30,節(jié)假日休息