欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

TypeScript之Generics泛型類型學(xué)習(xí)

 更新時(shí)間:2023年07月03日 09:48:32   作者:冴羽  
這篇文章主要為大家介紹了TypeScript之Generics泛型類型學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

TypeScript 的官方文檔早已更新,但我能找到的中文文檔都還停留在比較老的版本。所以對(duì)其中新增以及修訂較多的一些章節(jié)進(jìn)行了翻譯整理。

本篇整理自 TypeScript Handbook 中 「Generics」 章節(jié)。

本文并不嚴(yán)格按照原文翻譯,對(duì)部分內(nèi)容也做了解釋補(bǔ)充。

Generics 初探(Hello World of Generics)

軟件工程的一個(gè)重要部分就是構(gòu)建組件,組件不僅需要有定義良好和一致的 API,也需要是可復(fù)用的(reusable)。好的組件不僅能夠兼容今天的數(shù)據(jù)類型,也能適用于未來可能出現(xiàn)的數(shù)據(jù)類型,這在構(gòu)建大型軟件系統(tǒng)時(shí)會(huì)給你最大的靈活度。

在比如 C# 和 Java 語(yǔ)言中,用來創(chuàng)建可復(fù)用組件的工具,我們稱之為泛型(generics)。利用泛型,我們可以創(chuàng)建一個(gè)支持眾多類型的組件,這讓用戶可以使用自己的類型消費(fèi)(consume)這些組件。

讓我們開始寫第一個(gè)泛型,一個(gè)恒等函數(shù)(identity function)。所謂恒等函數(shù),就是一個(gè)返回任何傳進(jìn)內(nèi)容的函數(shù)。你也可以把它理解為類似于 echo 命令。

不借助泛型,我們也許需要給予恒等函數(shù)一個(gè)具體的類型:

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

或者,我們使用 any 類型:

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

盡管使用 any 類型可以讓我們接受任何類型的 arg 參數(shù),但也讓我們丟失了函數(shù)返回時(shí)的類型信息。如果我們傳入一個(gè)數(shù)字,我們唯一知道的信息是函數(shù)可以返回任何類型的值。

所以我們需要一種可以捕獲參數(shù)類型的方式,然后再用它表示返回值的類型。這里我們用了一個(gè)類型變量(type variable),一種用在類型而非值上的特殊的變量。

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

現(xiàn)在我們已經(jīng)給恒等函數(shù)加上了一個(gè)類型變量 Type,這個(gè) Type 允許我們捕獲用戶提供的類型,使得我們?cè)诮酉聛砜梢允褂眠@個(gè)類型。這里,我們?cè)俅斡?nbsp;Type 作為返回的值的類型。在現(xiàn)在的寫法里,我們可以清楚的知道參數(shù)和返回值的類型是同一個(gè)。

現(xiàn)在這個(gè)版本的恒等函數(shù)就是一個(gè)泛型,它可以支持傳入多種類型。不同于使用 any,它沒有丟失任何信息,就跟第一個(gè)使用 number 作為參數(shù)和返回值類型的的恒等函數(shù)一樣準(zhǔn)確。

在我們寫了一個(gè)泛型恒等函數(shù)后,我們有兩種方式可以調(diào)用它。第一種方式是傳入所有的參數(shù),包括類型參數(shù):

let output = identity<string>("myString"); // let output: string

在這里,我們使用 <> 而不是 ()包裹了參數(shù),并明確的設(shè)置 Type 為 string 作為函數(shù)調(diào)用的一個(gè)參數(shù)。

第二種方式可能更常見一些,這里我們使用了類型參數(shù)推斷(type argument inference)(部分中文文檔會(huì)翻譯為“類型推論”),我們希望編譯器能基于我們傳入的參數(shù)自動(dòng)推斷和設(shè)置 Type 的值。

let output = identity("myString"); // let output: string

注意這次我們并沒有用 <> 明確的傳入類型,當(dāng)編譯器看到 myString 這個(gè)值,就會(huì)自動(dòng)設(shè)置 Type 為它的類型(即 string)。

類型參數(shù)推斷是一個(gè)很有用的工具,它可以讓我們的代碼更短更易閱讀。而在一些更加復(fù)雜的例子中,當(dāng)編譯器推斷類型失敗,你才需要像上一個(gè)例子中那樣,明確的傳入?yún)?shù)。

使用泛型類型變量(Working with Generic Type Variables)

當(dāng)你創(chuàng)建類似于 identity 這樣的泛型函數(shù)時(shí),你會(huì)發(fā)現(xiàn),編譯器會(huì)強(qiáng)制你在函數(shù)體內(nèi),正確的使用這些類型參數(shù)。這就意味著,你必須認(rèn)真的對(duì)待這些參數(shù),考慮到他們可能是任何一個(gè),甚至是所有的類型(比如用了聯(lián)合類型)。

讓我們以 identity 函數(shù)為例:

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

如果我們想打印 arg 參數(shù)的長(zhǎng)度呢?我們也許會(huì)嘗試這樣寫:

function loggingIdentity<Type>(arg: Type): Type {
  console.log(arg.length);
    // Property 'length' does not exist on type 'Type'.
  return arg;
}

如果我們這樣做,編譯器會(huì)報(bào)錯(cuò),提示我們正在使用 arg 的 .length屬性,但是我們卻沒有在其他地方聲明 arg 有這個(gè)屬性。我們前面也說了這些類型變量代表了任何甚至所有類型。所以完全有可能,調(diào)用的時(shí)候傳入的是一個(gè) number 類型,但是 number 并沒有 .length 屬性。

現(xiàn)在假設(shè)這個(gè)函數(shù),使用的是 Type 類型的數(shù)組而不是 Type。因?yàn)槲覀兪褂玫氖菙?shù)組,.length 屬性肯定存在。我們就可以像創(chuàng)建其他類型的數(shù)組一樣寫:

function loggingIdentity<Type>(arg: Type[]): Type[] {
  console.log(arg.length);
  return arg;
}

你可以這樣理解 loggingIdentity 的類型:泛型函數(shù) loggingIdentity 接受一個(gè) Type 類型參數(shù)和一個(gè)實(shí)參 arg,實(shí)參 arg 是一個(gè) Type 類型的數(shù)組。而該函數(shù)返回一個(gè) Type 類型的數(shù)組。

如果我們傳入的是一個(gè)全是數(shù)字類型的數(shù)組,我們的返回值同樣是一個(gè)全是數(shù)字類型的數(shù)組,因?yàn)?nbsp;Type 會(huì)被當(dāng)成 number 傳入。

現(xiàn)在我們使用類型變量 Type,是作為我們使用的類型的一部分,而不是之前的一整個(gè)類型,這會(huì)給我們更大的自由度。

我們也可以這樣寫這個(gè)例子,效果是一樣的:

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

泛型類型 (Generic Types)

在上個(gè)章節(jié),我們已經(jīng)創(chuàng)建了一個(gè)泛型恒等函數(shù),可以支持傳入不同的類型。在這個(gè)章節(jié),我們探索函數(shù)本身的類型,以及如何創(chuàng)建泛型接口。

泛型函數(shù)的形式就跟其他非泛型函數(shù)的一樣,都需要先列一個(gè)類型參數(shù)列表,這有點(diǎn)像函數(shù)聲明:

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

泛型的類型參數(shù)可以使用不同的名字,只要數(shù)量和使用方式上一致即可:

function identity<Type>(arg: Type): Type {
  return arg;
}
let myIdentity: <Input>(arg: Input) => Input = identity;

我們也可以以對(duì)象類型的調(diào)用簽名的形式,書寫這個(gè)泛型類型:

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

這可以引導(dǎo)我們寫出第一個(gè)泛型接口,讓我們使用上個(gè)例子中的對(duì)象字面量,然后把它的代碼移動(dòng)到接口里:

interface GenericIdentityFn {
  <Type>(arg: Type): Type;
}
function identity<Type>(arg: Type): Type {
  return arg;
}
let myIdentity: GenericIdentityFn = identity;

有的時(shí)候,我們會(huì)希望將泛型參數(shù)作為整個(gè)接口的參數(shù),這可以讓我們清楚的知道傳入的是什么參數(shù) (舉個(gè)例子:Dictionary<string> 而不是 Dictionary)。而且接口里其他的成員也可以看到。

interface GenericIdentityFn<Type> {
  (arg: Type): Type;
}
function identity<Type>(arg: Type): Type {
  return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;

注意在這個(gè)例子里,我們只做了少許改動(dòng)。不再描述一個(gè)泛型函數(shù),而是將一個(gè)非泛型函數(shù)簽名,作為泛型類型的一部分。

現(xiàn)在當(dāng)我們使用 GenericIdentityFn 的時(shí)候,需要明確給出參數(shù)的類型。(在這個(gè)例子中,是 number),有效的鎖定了調(diào)用簽名使用的類型。

當(dāng)要描述一個(gè)包含泛型的類型時(shí),理解什么時(shí)候把類型參數(shù)放在調(diào)用簽名里,什么時(shí)候把它放在接口里是很有用的。

除了泛型接口之外,我們也可以創(chuàng)建泛型類。注意,不可能創(chuàng)建泛型枚舉類型和泛型命名空間。

泛型類(Generic Classes)

泛型類寫法上類似于泛型接口。在類名后面,使用尖括號(hào)中 <> 包裹住類型參數(shù)列表:

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

在這個(gè)例子中,并沒有限制你只能使用 number 類型。我們也可以使用 string 甚至更復(fù)雜的類型:

let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function (x, y) {
  return x + y;
};
console.log(stringNumeric.add(stringNumeric.zeroValue, "test"));

就像接口一樣,把類型參數(shù)放在類上,可以確保類中的所有屬性都使用了相同的類型。

正如我們?cè)?Class 章節(jié)提過的,一個(gè)類它的類型有兩部分:靜態(tài)部分和實(shí)例部分。泛型類僅僅對(duì)實(shí)例部分生效,所以當(dāng)我們使用類的時(shí)候,注意靜態(tài)成員并不能使用類型參數(shù)。

泛型約束(Generic Constraints)

在早一點(diǎn)的 loggingIdentity 例子中,我們想要獲取參數(shù) arg 的 .length 屬性,但是編譯器并不能證明每種類型都有 .length 屬性,所以它會(huì)提示錯(cuò)誤:

function loggingIdentity&lt;Type&gt;(arg: Type): Type {
  console.log(arg.length);
  // Property 'length' does not exist on type 'Type'.
  return arg;
}

相比于能兼容任何類型,我們更愿意約束這個(gè)函數(shù),讓它只能使用帶有 .length 屬性的類型。只要類型有這個(gè)成員,我們就允許使用它,但必須至少要有這個(gè)成員。為此,我們需要列出對(duì) Type 約束中的必要條件。

為此,我們需要?jiǎng)?chuàng)建一個(gè)接口,用來描述約束。這里,我們創(chuàng)建了一個(gè)只有 .length 屬性的接口,然后我們使用這個(gè)接口和 extend關(guān)鍵詞實(shí)現(xiàn)了約束:

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

現(xiàn)在這個(gè)泛型函數(shù)被約束了,它不再適用于所有類型:

loggingIdentity(3);
// Argument of type 'number' is not assignable to parameter of type 'Lengthwise'.

我們需要傳入符合約束條件的值:

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

在泛型約束中使用類型參數(shù)(Using Type Parameters in Generic Constraints)

你可以聲明一個(gè)類型參數(shù),這個(gè)類型參數(shù)被其他類型參數(shù)約束。
?
舉個(gè)例子,我們希望獲取一個(gè)對(duì)象給定屬性名的值,為此,我們需要確保我們不會(huì)獲取 obj 上不存在的屬性。所以我們?cè)趦蓚€(gè)類型之間建立一個(gè)約束:

function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
  return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a");
getProperty(x, "m");
// Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.

在泛型中使用類類型(Using Class Types in Generics)

在 TypeScript 中,當(dāng)使用工廠模式創(chuàng)建實(shí)例的時(shí)候,有必要通過他們的構(gòu)造函數(shù)推斷出類的類型,舉個(gè)例子:

function create<Type>(c: { new (): Type }): Type {
  return new c();
}

下面是一個(gè)更復(fù)雜的例子,使用原型屬性推斷和約束,構(gòu)造函數(shù)和類實(shí)例的關(guān)系。

class BeeKeeper {
  hasMask: boolean = true;
}
class ZooKeeper {
  nametag: string = "Mikle";
}
class Animal {
  numLegs: number = 4;
}
class Bee extends Animal {
  keeper: BeeKeeper = new BeeKeeper();
}
class Lion extends Animal {
  keeper: ZooKeeper = new ZooKeeper();
}
function createInstance<A extends Animal>(c: new () => A): A {
  return new c();
}
createInstance(Lion).keeper.nametag;
createInstance(Bee).keeper.hasMask;

TypeScript 系列

TypeScript 中文手冊(cè) https://typescript.bootcss.com/

TypeScript 系列文章由官方文檔翻譯、重難點(diǎn)解析、實(shí)戰(zhàn)技巧三個(gè)部分組成,涵蓋入門、進(jìn)階、實(shí)戰(zhàn),旨在為你提供一個(gè)系統(tǒng)學(xué)習(xí) TS 的教程,更多關(guān)于TypeScript Generics泛型的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • type-challenge刷題(easy部分)示例詳解

    type-challenge刷題(easy部分)示例詳解

    這篇文章主要為大家介紹了type-challenge刷題(easy部分)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • typescript type 分配條件類型示例詳解

    typescript type 分配條件類型示例詳解

    這篇文章主要為大家介紹了typescript type 分配條件類型示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • TypeScript開發(fā)HapiJS應(yīng)用詳解

    TypeScript開發(fā)HapiJS應(yīng)用詳解

    這篇文章主要為大家介紹了TypeScript開發(fā)HapiJS應(yīng)用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • TypeScript 交叉類型使用方法示例總結(jié)

    TypeScript 交叉類型使用方法示例總結(jié)

    這篇文章主要為大家介紹了TypeScript 交叉類型使用方法示例總結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • Manipulation-TypeScript?DOM操作示例解析

    Manipulation-TypeScript?DOM操作示例解析

    這篇文章主要為大家介紹了DOM?Manipulation-TypeScript?DOM操作示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • 微信小程序?qū)崿F(xiàn)圖片預(yù)加載組件

    微信小程序?qū)崿F(xiàn)圖片預(yù)加載組件

    預(yù)加載圖片是提高用戶體驗(yàn)的一個(gè)很好方法。圖片預(yù)先加載到瀏覽器中,訪問者便可順利地在你的網(wǎng)站上沖浪,并享受到極快的加載速度。下面這篇文章主要介紹了微信小程序?qū)崿F(xiàn)圖片預(yù)加載組件的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-01-01
  • Three.js引入Cannon.js及使用示例詳解

    Three.js引入Cannon.js及使用示例詳解

    這篇文章主要為大家介紹了Three.js引入Cannon.js及使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • js 獲取今天以及過去日期

    js 獲取今天以及過去日期

    這篇文章主要介紹了js獲得當(dāng)前系統(tǒng)日期時(shí)間以及過去系統(tǒng)日期時(shí)間的方法,涉及javascript操作日期時(shí)間的相關(guān)技巧,示例代碼如下,需要的朋友可以參考下
    2017-04-04
  • Webpack source map實(shí)戰(zhàn)分析詳解

    Webpack source map實(shí)戰(zhàn)分析詳解

    這篇文章主要為大家介紹了Webpack source map示例分析詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • typescript快速上手的基礎(chǔ)知識(shí)篇

    typescript快速上手的基礎(chǔ)知識(shí)篇

    靜態(tài)類型的typescript與傳統(tǒng)動(dòng)態(tài)弱類型語(yǔ)言javascript不同,在執(zhí)行前會(huì)先編譯成javascript,因?yàn)樗鼜?qiáng)大的type類型系統(tǒng)加持,能讓我們?cè)诰帉懘a時(shí)增加更多嚴(yán)謹(jǐn)?shù)南拗?。注意,它并不是一門全新的語(yǔ)言,所以并沒有增加額外的學(xué)習(xí)成本
    2022-12-12

最新評(píng)論