精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

一文讀懂 TypeScript 泛型及應用

開發
本文詳細解釋了適用于函數、接口、類和自定義類型的泛型,還使用泛型創建映射類型和條件類型。這些中的每一個都使泛型成為使用 TypeScript 時的強大工具。正確的使用泛型將避免一遍又一遍地重復代碼,并使編寫的類型更加靈活。

泛型是靜態類型語言的基本特征,允許將類型作為參數傳遞給另一個類型、函數、或者其他結構。TypeScript 支持泛型作為將類型安全引入組件的一種方式。這些組件接受參數和返回值,其類型將是不確定的,直到它在代碼中被使用。下面將通過一些示例,探索如何在函數、類型、類和接口中使用泛型,以及使用泛型創建映射類型和條件類型。

1. 泛型語法

首先來看看TypeScript 泛型的語法。泛型的語法為 <T>,其中 T 表示傳入的類型。在這種情況下,T 和函數參數的工作方式相同,其作為將在創建結構實例時聲明的類型的占位符。因此,尖括號內指定的泛型類型也稱為泛型類型參數。泛型的定義可以有多個泛型類型采參數,例如:<T, K, Z>。

注意:通常使用單個字母來命名泛型類型。這不是語法規則,我們也可以像 TypeScript 中的任何其他類型一樣命名泛型,但這種約定有助于向閱讀代碼的人傳達泛型類型不需要特定類型。

下面通過一個函數的例子來看看泛型的基本語法。假設有一個 JavaScript 函數,它接受兩個參數:一個對象和一個包含key的數組。該函數將基于原始對象返回一個新對象,其僅包含想要的key:

function pickObjectKeys(obj, keys) {
  let result = {}
  for (const key of keys) {
    if (key in obj) {
      result[key] = obj[key]
    }
  }
  return result
}

在 pickObjectKeys() 函數中,遍歷了keys數組并使用數組中指定的key創建一個新對象。下面來測試一下這個函數:

const language = {
  name: "TypeScript",
  age: 8,
  extensions: ['ts', 'tsx']
}

const ageAndExtensions = pickObjectKeys(language, ['age', 'extensions'])

這里聲明了一個language對象,然后使用 pickObjectKeys() 函數將 language 對象中的 age 和 extensions 屬性組成了一個新的對象 ageAndExtensions,其值如下:

{
  age: 8,
  extensions: ['ts', 'tsx']
}

如果想將這個函數遷移到 TypeScript 以使其類型安全,則可以使用泛型。重構的代碼如下:

function pickObjectKeys<T, K extends keyof T>(obj: T, keys: K[]) {
  let result = {} as Pick<T, K>
  for (const key of keys) {
    if (key in obj) {
      result[key] = obj[key]
    }
  }
  return result
}

const language = {
  name: "TypeScript",
  age: 8,
  extensions: ['ts', 'tsx']
}

const ageAndExtensions = pickObjectKeys(language, ['age', 'extensions'])

<T, K extends keyof T> 為函數聲明了兩個參數類型,其中 K 被分配給了一個類型,該類型是 T 中的 key 的集合。然后將 obj 參數設置為 T,表示任何類型,并將 keys 設置為數組,無論 K 是什么類型。

當傳入的 obj 參數為language 對象時,T將 age 設置為number類型,將 extensions 設置為string[]類型,所以變量 ageAndExtensions  的類型為:

{
  age: number;
  extensions: string[];
}

這樣就會根據提供給 pickObjectKeys 的參數來判斷返回值的類型,從而允許函數在知道需要強制執行的特定類型之前靈活地強制執行類型結構。當在 Visual Studio Code 等 IDE 中使用該函數時,這使得開發體驗更好,它將根據提供的對象為 keys 參數提供建議:

圖片圖片

2. 在函數中使用泛型

將泛型與函數一起使用的最常見場景之一就是,當有一些不容易為所有的用例定義類型時,為了使該函數適用于更多情況,就可以使用泛型來定義。下面來看看在函數中使用泛型的常見場景。

(1)分配泛型參數

先來看下面的函數,它返回函數參數傳入的內容:

function identity(value) {
  return value;
}

可以為其添加泛型類型以使函數的類型更安全:

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

這里將函數轉化為接受泛型類型參數 T 的泛型函數,它第一個參數的類型,然后將返回類型也設置為 T 。下面來測試一下這個函數:

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

const result = identity(123);

result 的類型為 123,這是我們傳入的數字:

圖片圖片

此時,TypeScript 使用調用代碼本身來推斷泛型類型。這樣調用代碼不需要傳遞任何類型參數。當然,我們也可以顯式地將泛型類型參數設置為想要的類型:

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

const result = identity<number>(123);

在這段代碼中,result的類型就是 number:

圖片圖片

這里使用 <number> 定義了傳入類型,讓 TypeScript 標識函數的泛型類型參數 T 為 number 類型。這將強制number類型作為參數和返回值的類型。當再傳入其他類型時,就會報錯:

圖片圖片

(2)直接傳遞類型參數

在使用自定義類型時,直接傳遞類型參數也很有用。來看下面的代碼:

type ProgrammingLanguage = {
  name: string;
};

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

const result = identity<ProgrammingLanguage>({ name: "TypeScript" });

在這段代碼中,result 為自定義類型 ProgrammingLanguage,它直接傳遞給了 identity 函數。如果沒有顯式地定義類型參數,則result的類型就是 { name: string } 。

另一個常見的例子就是使用函數從 API 獲取數據:

async function fetchApi(path: string) {
  const response = await fetch(`https://example.com/api${path}`)
  return response.json();
}

這個異步函數將 URL 路徑path作為參數,使用 fetch API 向 URL 發出請求,然后返回 JSON 響應值。在這種情況下,fetchApi 函數的返回類型是 Promise<any>,這是 fetch 的響應對象的 json() 調用的返回類型。

將 any 作為返回類型并不會有任何作用,它表示任意類型,使用它將失去靜態類型檢查。如果我們知道 API 將返回指定結構的對象,則可以使用泛型以使此函數類型更安全:

async function fetchApi<ResultType>(path: string): Promise<ResultType> {
  const response = await fetch(`https://example.com/api${path}`);
  return response.json();
}

這里就將函數轉換為接受 ResultType 泛型類型參數的泛型函數。此泛型類型用于函數的返回類型:Promise<ResultType>。

注意:由于這個函數是異步的,因此會返回一個 Promise 對象。TypeScript 中的 Promise 類型本身是一個泛型類型,它接受 Promise 解析為的值的類型。

可以看到,泛型并沒有在參數列表中使用,也沒有在TypeScript能夠推斷其值的其他地方使用。這意味著在調用函數時,必須顯式地傳遞此泛型的類型:

type User = {
  name: string;
}

async function fetchApi<ResultType>(path: string): Promise<ResultType> {
  const response = await fetch(`https://example.com/api${path}`);
  return response.json();
}

const data = await fetchApi<User[]>('/users')

在這段代碼中,創建了一個名為 User 的新類型,并使用該類型的數組 (User[]) 作為 ResultType 泛型參數的類型。data 變量現在的類型是 User[] 而不是 any。

注意:當使用 await 異步處理函數的結果時,返回類型將是 Promise<T> 中的 T 類型,在這個示例中就是泛型類型 ResultType。

(3)默認類型參數

在上面 fetchApi 函數的例子中,調用代碼時必須提供類型參數。如果調用代碼不包含泛型類型參數,則 ResultType 將推斷為 unknow。來看下面的例子:

async function fetchApi<ResultType>(path: string): Promise<ResultType> {
  const response = await fetch(`https://example.com/api${path}`);
  return 
response.json();
}

const data = await fetchApi('/users')

console.log(data.a)

這段代碼嘗試訪問data的a屬性,但是由于data是unknow類型,將無法訪問對象的屬性。

如果不打算為泛型函數的每次調用添加特定的類型,則可以為泛型類型參數添加默認類型。通過在泛型類型參數后面添加 = DefaultType 來完成:

async function fetchApi<ResultType = Record<string, any>>(path: string): Promise<ResultType> {
  const response = await fetch(`https://example.com/api${path}`);
  return response.json();
}

const data = await fetchApi('/users')

console.log(data.a)

這里不需要在調用 fetchApi 函數時將類型傳遞給 ResultType 泛型參數,因為它具有默認類型 Record<string, any>。這意味著 TypeScript 會將data識別為具有string類型的鍵和any類型值的對象,從而允許訪問其屬性。

(4)類型參數約束

在某些情況下,泛型類型參數只允許將某些類型傳遞到泛型中,這時就可以對參數添加約束。

假如有一個存儲限制,只能存儲所有屬性值都為字符串類型的對象。因此,可以創建一個函數,該函數接受任何對象并返回另一個對象,其 key 值與原始對象相同,但所有值都轉換為字符串。代碼如下:

function stringifyObjectKeyValues<T extends Record<string, any>>(obj: T) {
  return Object.keys(obj).reduce((acc, key) =>  ({
    ...acc,
    [key]: JSON.stringify(obj[key])
  }), {} as { [K in keyof T]: string })
}

在這段代碼中,stringifyObjectKeyValues 函數使用 reduce 數組方法遍歷包含原始對象的key的數組,將屬性值字符串化并將它們添加到新數組中。

為確保調用代碼始終傳入一個對象作為參數,可以在泛型類型 T 上使用類型約束:

function stringifyObjectKeyValues<T extends Record<string, any>>(obj: T) {
  // ...
}

extends Record<string, any> 被稱為泛型類型約束,它允許指定泛型類型必須可分配給 extends 關鍵字之后的類型。在這種情況下,Record<string, any> 表示具有string類型的鍵和any類型的值的對象。我們可以使類型參數擴展任何有效的 TypeScript 類型。

在調用reduce時,reducer函數的返回類型是基于累加器的初始值。{} as { [K in keyof T]: string } 通過對空對象 {} 使用類型斷言將累加器的初始值的類型設置為{ [K in keyof T]: string }。type { [K in keyof T]: string } 創建了一個新類型,其鍵與 T 相同,但所有值都設置為字符串類型,這稱為映射類型。

下面來測試一下這個函數:

function stringifyObjectKeyValues<T extends Record<string, any>>(obj: T) {
  return Object.keys(obj).reduce((acc, key) =>  ({
    ...acc,
    [key]: JSON.stringify(obj[key])
  }), {} as { [K in keyof T]: string })
}

const stringifiedValues = stringifyObjectKeyValues({ a: "1", b: 2, c: true, d: [1, 2, 3]})

變量 stringifiedValues 的類型如下:

{
  a: string;
  b: string;
  c: string;
  d: string;
}

3. 在接口、類和類型中使用泛型

在 TypeScript 中創建接口和類時,使用泛型類型參數來設置結果對象的類型會很有用。例如,一個類可能具有不同類型的屬性,具體取決于傳入構造函數的內容。下面就來看看在類和接口中聲明泛型類型參數的語法。

(1)接口和類中的泛型

要創建泛型接口,可以在接口名稱后添加類型參數列表:

interface MyInterface<T> {
  field: T
}

這里聲明了一個具有field字段的接口,field字段的類型由傳入 T 的類型確定。

對于類,它的語法和接口定義幾乎是相同的:

class MyClass<T> {
  field: T
  constructor(field: T) {
    this.field = field
  }
}

通用接口/類的一個常見例子就是當有一個類型取決于如何使用接口/類的字段。假設有一個 HttpApplication 類,用于處理對 API 的 HTTP 請求,并且某些 context 值將被傳遞給每個請求處理程序。代碼如下:

class HttpApplication<Context> {
  context: Context
 constructor(context: Context) {
    this.context = context;
  }

  // ... 

  get(url: string, handler: (context: Context) => Promise<void>): this {
    // ... 
    return this;
  }
}

這個類儲存了一個 context,它的類型作為 get 方法中handler函數的參數類型傳入。在使用時,傳遞給 get 方法的handler的參數類型將從傳遞給類構造函數的內容中推斷出來:

const context = { someValue: true };
const app = new HttpApplication(context);

app.get('/api', async () => {
  console.log(context.someValue)
});

在這段代碼中,TypeScript 會將 context.someValue 的類型推斷為 boolean。

(2)自定義類型中的泛型

將泛型應用于類型的語法類似于它們應用于接口和類的方式。來看下面的代碼:

type MyIdentityType<T> = T

這個泛型類型返回類型參數傳遞的類型。使用以下代碼來實現這種類型:

type B = MyIdentityType<number>

在這種情況下,B 的類型就是number。

泛型類型通常用于創建工具類型,尤其是在使用映射類型時。TypeScript 內置了許多工具類型。例如 Partial實用工具類型,它傳入類型 T 并返回另一種具有與 T 相同的類型,但它們的所有字段都設置為可選。Partial的實現如下:

type Partial<T> = {
  [P in keyof T]?: T[P];
};

這里,Partial 接受一個類型,遍歷它的屬性類型,然后將它們作為可選的新類型返回。

注意:由于 Partial 已經內置到了 TypeScript 中,因此將此代碼編譯到 TypeScript 環境中會重新聲明 Partial 并引發錯誤。上面的 Partial 實現僅用于說明目的。

4. 使用泛型創建映射類型

使用 TypeScript 時,有時需要創建一個與另一種類型具有相同結構的類型。這意味著它們應該具有相同的屬性,但屬性的類型不同。對于這種情況,使用映射類型可以重用初始類型并減少重復代碼。這種結構稱為映射類型并依賴于泛型。下面就來看看如何創建映射類型。

先來看一個例子,給定一種類型,返回一個新類型,其中所有屬性值都設置為 boolean 類型。可以使用以下代碼創建此類型:

type BooleanFields<T> = {
  [K in keyof T]: boolean;
}

在這種類型中,使用 [K in keyof T] 指定新類型將具有的屬性。keyof T 用于返回 T 中所有可用屬性的名稱。然后使用 K in 來指定新類型的屬性是keyof T返回的類型中可用的所有屬性。

這將創建一個名為 K 的新類型,該類型就是當前屬性的名稱。可以用于使用 T[K] 語法來訪問原始類型中此屬性的類型。在這種情況下,將屬性值的類型設置為 boolean。

這種 BooleanFields 類型的一個使用場景是創建一個選項對象。假設有一個數據庫模型,例如 User。從數據庫中獲取此模型的記錄時,還可以傳遞一個指定要返回哪些字段的對象。該對象將具有與模型相同的屬性,但類型設置為布爾值。在字段中傳遞 true 意味著希望它被返回,而 false 則意味著希望它被省略。

可以在現有模型類型上使用 BooleanFields 泛型來返回與模型具有相同結構的新類型,但所有字段都設置為布爾類型,代碼如下所示:

type BooleanFields<T> = {
  [K in keyof T]: boolean;
};

type User = {
  email: string;
  name: string;
}

type UserFetchOptions = BooleanFields<User>;

UserFetchOptions 的類型如下:

type UserFetchOptions = {
  email: boolean;
  name: boolean;
}

在創建映射類型時,還可以為字段提供修飾符,例如 Readonly<T>。Readonly<T> 類型返回一個新類型,其中傳遞類型的所有屬性都設置為只讀屬性。這種類型的實現如下:

type Readonly<T> = {
  readonly [K in keyof T]: T[K]
}

注意:由于 Readonly 已經內置到 TypeScript 中,因此將此代碼編譯到您的 TypeScript 環境中會重新聲明 Readonly 并引發錯誤。此處引用的 Readonly 實現僅用于說明目的。

目前,可以在映射類型中使用的兩個可用修飾符是 readonly 修飾符,它必須作為前綴添加到屬性中,用于將屬性設置為只讀;以及?修飾符,可以作為后綴添加到屬性中,用于將屬性設置為可選。

5. 使用泛型創建條件類型

下面來看看如何使用泛型創建條件類型。

(1)基礎條件類型

條件類型是泛型類型,根據某些條件具有不同的結果類型。先來看看下面的泛型類型 IsStringType<T>:

type IsStringType<T> = T extends string ? true : false;

在這段代碼中,創建了一個名為 IsStringType 的新泛型類型,它接收一個類型參數 T。在類型定義中,使用的語法類似于 JavaScript 中的三元表達式。此條件表達式檢查類型 T 是否是 string 類型。如果是,結果的類型將是 true;否則,結果的類型將是 false 。

要嘗試這種條件類型,需要將類型作為其類型參數傳遞:

type IsStringType<T> = T extends string ? true : false;

type A = "abc";
type B = {
  name: string;
};

type ResultA = IsStringType<A>;
type ResultB = IsStringType<B>;

在此代碼中,創建了兩種類型:A 和 B。A 是字符串字面量類型 abc,B 是具有string類型的name屬性的對象的類型。將這兩種類型與 IsStringType 條件類型一起使用,并將結果類型存儲到兩個新類型 ResultA 和 ResultB 中。

這里 ResultA 類型設置為 true,而 ResultB 類型設置為 false。因為 A 確實擴展了字符串類型,而 B 沒有擴展字符串類型,因為它被設置為具有字符串類型的單個name屬性的對象的類型。

條件類型的一個有用特性是它允許使用特殊關鍵字 infer 在 extends 中推斷類型信息。可以在條件為真的分支中使用這種新類型。此功能的一種用途是檢索任何函數類型的返回類型。

例如,GetReturnType 類型定義如下:

type GetReturnType<T> = T extends (...args: any[]) => infer U ? U : never;

在這段代碼中,創建了一個新的泛型類型,它是一個名為 GetReturnType 的條件類型。此泛型類型接受一個類型參數 T。在類型聲明本身內部,檢查類型 T 是否擴展了與接受可變數量參數(包括0)的函數簽名匹配的類型,然后推斷該返回函數的類型,創建一個新類型 U,它可用于條件的真實分支。U 的類型將綁定到傳遞函數的返回值的類型。如果傳遞的類型 T 不是函數,則代碼將返回類型nerver。

將此類型與以下代碼一起使用:

type GetReturnType<T> = T extends (...args: any[]) => infer U ? U : never;

function someFunction() {
  return true;
}

type ReturnTypeOfSomeFunction = GetReturnType<typeof someFunction>;

在這段代碼中,創建了一個名為 someFunction 的函數,該函數返回 true。然后,使用 typeof 運算符將此函數的類型傳遞給 GetReturnType 泛型,并將結果類型保存在 ReturnTypeOfSomeFunction 中。

由于 someFunction 變量的類型是函數,因此條件類型將計算條件為真的分支。這將返回類型 U 作為結果。U類型是根據函數的返回類型推斷出來的,在本例中是boolean。如果檢查 ReturnTypeOfSomeFunction 的類型,會發現它被設置為了boolean類型。

(2)高級條件類型

條件類型是 TypeScript 中最靈活的功能之一,允許創建一些高級實用程序類型。接下來就創建一個名為 NestedOmit<T, KeysToOmit> 的類型,它可以省略對象中的字段,就像現有的 Omit<T, KeysToOmit> 實用程序類型一樣,但也允許使用點表示法省略嵌套字段。

使用新的 NestedOmit<T, KeysToOmit> 泛型,將能夠使用下面例子中的類型:

type SomeType = {
  a: {
    b: string,
    c: {
      d: number;
      e: string[]
    },
    f: number
  }
  g: number | string,
  h: {
    i: string,
    j: number,
  },
  k: {
    l: number,<F3>
  }
}

type Result = NestedOmit<SomeType, "a.b" | "a.c.e" | "h.i" | "k">;

這段代碼聲明了一個名為 SomeType 的類型,該類型具有嵌套屬性的多級結構。使用 NestedOmit 泛型傳入類型,然后列出想要省略的屬性的key。第二個類型參數中使用點符號來標識要省略的鍵。然后將結果類型存儲在 Result 中。

構造此條件類型將使用 TypeScript 中的許很多功能,例如模板文本類型、泛型、條件類型和映射類型。

首先創建一個名為 NestedOmit 的泛型類型,它接受兩個類型參數:

type NestedOmit<T extends Record<string, any>, KeysToOmit extends string>

第一個類型參數為 T,它必須是可分配給 Record<string, any> 類型的類型,它是要從中省略屬性的對象的類型。第二個類型參數為 KeysToOmit,它必須是string類型。使用它來指定要從類型 T 中省略的key。

接下來需要判斷 KeysToOmit 是否可分配給類型 ${infer KeyPart1}.${infer KeyPart2}:

type NestedOmit<T extends Record<string, any>, KeysToOmit extends string> =
  KeysToOmit extends `${infer KeyPart1}.${infer KeyPart2}`

這里就用到了模板文本類型,同時利用條件類型在模板文字中推斷出其他兩種類型。通過這兩部分,將一個字符串拆分為了兩個字符串。第一部分將分配給類型 KeyPart1 并將包含第一個點之前的所有內容。第二部分將分配給類型 KeyPart2 并將包含第一個點之后的所有內容。如果將“a.b.c”作為 KeysToOmit 傳遞,則最初 KeyPart1 將設置為字符串類型“a”,而 KeyPart2 將設置為“b.c”。

接下來,使用三元表達式來定義條件為true的分支:

type NestedOmit<T extends Record<string, any>, KeysToOmit extends string> =
  KeysToOmit extends `${infer KeyPart1}.${infer KeyPart2}`
    ?
      KeyPart1 extends keyof T

這里使用 KeyPart1 extends keyof T 來檢查 KeyPart1 是否是給定類型 T 的有效屬性。如果是一個有效的 key,使用以下代碼以使條件計算為兩種類型之間的交集:

type NestedOmit<T extends Record<string, any>, KeysToOmit extends string> =
  KeysToOmit extends `${infer KeyPart1}.${infer KeyPart2}`
    ?
      KeyPart1 extends keyof T
      ?
        Omit<T, KeyPart1>
        & {
          [NewKeys in KeyPart1]: NestedOmit<T[NewKeys], KeyPart2>
        }

Omit<T, KeyPart1> 是使用 TypeScript 自帶的 Omit 構建的類型。此時,KeyPart1 不是點表示法:它將包含一個字段的確切名稱,該字段包含要從原始類型中省略的嵌套字段。因此,可以安全地使用現有的實用程序類型。

使用 Omit 刪除 T[KeyPart1] 內的一些嵌套字段,為此,必須重新生成 T[KeyPart1] 的類型。為避免重新生成整個 T 類型,使用 Omit 從 T 中僅刪除 KeyPart1,保留其他字段。然后,將在下一部分的類型中重建 T[KeyPart1]。

[NewKeys in KeyPart1]:NestedOmit<T[NewKeys], KeyPart2> 是一個映射類型,其中屬性是可分配給 KeyPart1 的屬性,也就是上面從 KeysToOmit 中提取的部分。這是需要刪除的字段的父級。如果傳入了 a.b.c,那么在第一次它將是a中的 NewKeys。然后,將此屬性的類型設置為遞歸調用 NestedOmit 實用程序類型的結果,但現在使用 T[NewKeys] 作為第一個類型參數傳遞 T 內的此屬性的類型,并作為第二個類型參數傳遞點符號的其余key,在 KeyPart2 中可用。

在內部條件為 false 分支中,返回綁定到 T 的當前類型,就好像 KeyPart1 不是 T的有效key:

type NestedOmit<T extends Record<string, any>, KeysToOmit extends string> =
  KeysToOmit extends `${infer KeyPart1}.${infer KeyPart2}`
    ?
      KeyPart1 extends keyof T
      ?
        Omit<T, KeyPart1>
        & {
          [NewKeys in KeyPart1]: NestedOmit<T[NewKeys], KeyPart2>
        }
      : T

條件的這個分支意味著省略 T 中不存在的字段。在這種情況下,無需再進一步。最后,在外部條件為 false的分支中,使用內置的 Omit 實用程序類型從 T 中省略 KeysToOmit:

type NestedOmit<T extends Record<string, any>, KeysToOmit extends string> =
  KeysToOmit extends `${infer KeyPart1}.${infer KeyPart2}`
    ?
      KeyPart1 extends keyof T
      ?
        Omit<T, KeyPart1>
        & {
          [NewKeys in KeyPart1]: NestedOmit<T[NewKeys], KeyPart2>
        }
      : T
    : Omit<T, KeysToOmit>;

如果條件 KeysToOmit extends ${infer KeyPart1}.${infer KeyPart2} 為 false,則表示 KeysToOmit 未使用點表示法,因此可以使用 Omit 實用程序類型。

現在,要使用新的 NestedOmit 條件類型,創建一個名為 NestedObject 的類型:

type NestedObject = {
  a: {
    b: {
      c: number;
      d: number;
    };
    e: number;
  };
  f: number;
};

調用 NestedOmit 以省略 a.b.c 中可用的嵌套字段:

type Result = NestedOmit<NestedObject, "a.b.c">;

在條件類型的第一次計算中,外部條件為真,因為字符串字面量類型a.b.c可分配給模板文本類型${infer KeyPart1}.${infer KeyPart2}。在這種情況下,KeyPart1 將被推斷為字符串字面量類型a,而 KeyPart2 將被推斷為字符串的剩余部分,在本例中為b.c。

下面將計算內部條件,結果為真,因為此時 KeyPart1 是 T 的鍵。KeyPart1 現在是a,并且 T 確實具有屬性a:

type NestedObject = {
  a: {
    b: {
      c: number;
      d: number;
    };
    e: number;
  };
  f: number;
};

繼續計算條件,現在位于內部 true分支中。這構建了一個新類型,它是其他兩種類型的交集。第一種類型是在 T 上使用 Omit 實用程序類型來省略可分配給 KeyPart1 的字段(在本例中為 a 字段)的結果。第二種類型是通過遞歸調用 NestedOmit 構建的新類型。

如果對 NestedOmit 進行下一步求值,對于第一次遞歸調用,交集類型會構建一個類型以用作 a 字段的類型。這將重新創建a字段,而不需要忽略嵌套字段。

在 NestedOmit 的最終計算中,第一個條件將返回 false,因為傳遞的字符串類型是c。這種情況下,可以使用內置類型從對象中省略該字段。這將返回 b 字段的類型,即省略 c 的原始類型。計算到此結束,TypeScript 返回了需要使用的新類型,省略了嵌套字段。

6. 小結

本文詳細解釋了適用于函數、接口、類和自定義類型的泛型,還使用泛型創建映射類型和條件類型。這些中的每一個都使泛型成為使用 TypeScript 時的強大工具。正確的使用泛型將避免一遍又一遍地重復代碼,并使編寫的類型更加靈活。

責任編輯:武曉燕 來源: 前端充電寶
相關推薦

2022-06-14 09:01:06

TypeScript泛型

2022-08-22 09:01:59

類型兼容性TypeScript

2021-08-04 16:06:45

DataOps智領云

2023-12-22 19:59:15

2022-09-22 09:00:46

CSS單位

2025-04-03 10:56:47

2018-09-28 14:06:25

前端緩存后端

2022-11-06 21:14:02

數據驅動架構數據

2014-04-29 09:59:44

2023-11-27 17:35:48

ComponentWeb外層

2022-07-05 06:30:54

云網絡網絡云原生

2023-05-20 17:58:31

低代碼軟件

2022-10-20 08:01:23

2022-12-01 17:23:45

2021-12-29 18:00:19

無損網絡網絡通信網絡

2025-10-14 09:01:20

2022-07-26 00:00:03

語言模型人工智能

2025-03-24 08:15:00

2021-05-18 09:48:58

前端開發架構
點贊
收藏

51CTO技術棧公眾號

亚洲图片欧美色图| 免费日韩av| 欧美视频一区二区在线观看| 亚洲精品二区| 国产免费视频一区二区三区| 欧美日韩一区自拍| 日韩高清免费在线| 91极品视频在线观看| √天堂8在线网| 成人激情综合网站| 国产精品第七十二页| 日韩在线一卡二卡| 国产精品对白久久久久粗| 日韩欧美亚洲一二三区| 亚洲毛片aa| 亚洲精品一区二区三区四区| 久久久www| 久久国产精品久久国产精品| 午夜一区二区三区免费| 久久久国产精品网站| 亚洲国产日韩av| 午夜欧美性电影| 懂色av成人一区二区三区| 丝袜国产日韩另类美女| 九九热r在线视频精品| 精品人妻互换一区二区三区| 看亚洲a级一级毛片| 色综合久久久久综合| 成人午夜免费剧场| caoporn国产精品免费视频| 懂色av一区二区夜夜嗨| 国产精品久久久久久久久久小说| 亚洲av午夜精品一区二区三区| av免费在线一区二区三区| 国产成人亚洲综合a∨婷婷图片 | 久久精视频免费在线久久完整在线看 | 黑鬼大战白妞高潮喷白浆| 日本亚洲精品| 久久免费的精品国产v∧| 不卡一卡2卡3卡4卡精品在| 国产精品第六页| 亚洲麻豆视频| 欧美激情在线视频二区| 日本猛少妇色xxxxx免费网站| 久久69成人| 精品久久久国产| 国产激情片在线观看| 在线激情免费视频| 国产婷婷精品av在线| 国产日韩在线一区二区三区| 国产高清第一页| 精品写真视频在线观看| 国产精品久久久久久av下载红粉| 手机看片国产日韩| 亚洲欧美日本伦理| 亚洲国产天堂久久国产91| aaa黄色大片| 日韩一区二区三区在线看| 91精品欧美综合在线观看最新| 激情成人开心网| 国产成人在线视频免费观看| 国产精品久久国产精麻豆99网站| 亚洲xxxxx性| 97国产精品久久久| 麻豆精品精品国产自在97香蕉| 欧美成人免费视频| 国产成人自拍网站| 亚洲激情中文| 久久成人免费视频| 欧美人妻精品一区二区免费看| 久久夜色电影| 亚洲精品久久久久久下一站| 香港三日本8a三级少妇三级99| 第四色男人最爱上成人网| 欧美午夜女人视频在线| 99爱视频在线| 素人一区二区三区| 欧美日韩中文字幕一区| 五月天av在线播放| 国产激情综合| 日韩免费性生活视频播放| 一级黄色免费视频| 在线日韩网站| 伊人男人综合视频网| 亚洲综合久久av一区二区三区| 婷婷亚洲精品| 亚洲天堂一区二区三区| 五月天免费网站| 一本一道久久a久久精品蜜桃| 亚洲精品一二区| 婷婷色一区二区三区| 成人情趣视频| 欧美成人性色生活仑片| 日韩黄色精品视频| 爽好多水快深点欧美视频| 国产在线视频一区| 欧美 日韩 综合| 久久久国产一区二区三区四区小说 | 一区二区三区成人精品| 欧美中文字幕视频| 艳妇乳肉豪妇荡乳av| 国产iv一区二区三区| 久久99精品久久久久久青青日本 | 日韩美女一区二区三区| 亚洲av成人片色在线观看高潮| 国产精品一区二区三区av| 亚洲成人性视频| 微拍福利一区二区| 午夜亚洲福利| 欧美在线视频观看| 国产精品热久久| 99国产欧美另类久久久精品 | jizzjizz国产精品喷水| 草民电影神马电影一区二区| 精品国产91乱码一区二区三区| 在线黄色免费看| 精品精品国产毛片在线看| 色小说视频一区| 日本免费观看视| 久久99最新地址| 久久久久久久免费| 自拍亚洲图区| 欧美日韩一区二区三区高清 | 亚洲图片第一页| 亚洲激情欧美| 国产日韩欧美在线视频观看| 日韩毛片在线一区二区毛片| 亚洲精品欧美综合四区| 高清一区二区视频| 欧洲亚洲视频| 欧美丰满少妇xxxxx做受| 亚洲第一区av| www.成人在线| 潘金莲一级淫片aaaaaa播放1| 久久bbxx| 欧美性开放视频| 亚洲av永久无码精品| 一本到12不卡视频在线dvd| 国产精品久久久久影院日本| 日本美女一级片| 亚洲摸摸操操av| 在线黄色免费观看| 国产在线日韩精品| 91av在线免费观看视频| 丰满肉嫩西川结衣av| 日韩一区日韩二区| 在线免费视频一区| 狠狠色狠狠色综合婷婷tag| 欧美亚洲另类在线| 无码国产色欲xxxx视频| 亚洲va欧美va人人爽| 国产人妖在线观看| 欧美午夜不卡| 99久久99久久| 欧美一卡二卡| 日韩你懂的在线播放| 欧美精品一区二区成人| 国产精品一区免费在线观看| 亚洲人久久久| 日韩三级成人| 久久久av一区| 国产成人毛毛毛片| 亚洲一区免费观看| 漂亮人妻被黑人久久精品| 亚洲美女网站| 免费成人看片网址| 朝桐光一区二区| 中文在线不卡视频| 亚洲在线视频播放| 亚洲精品免费播放| 一级少妇精品久久久久久久| 亚洲国产三级| 欧美午夜欧美| 日日夜夜综合| 欧美剧在线观看| 欧美一级在线免费观看| 欧美视频裸体精品| 女人黄色一级片| 精品在线一区二区三区| 国产精品免费看久久久无码| 国产亚洲成av人片在线观黄桃| 日韩中文字幕视频在线| 国产精品人人爽| 亚洲宅男天堂在线观看无病毒| 在线看的黄色网址| 婷婷亚洲五月| 国产高清自拍一区| 欧美精品日日操| 色吧影院999| 性生活免费网站| 黑人巨大精品欧美一区二区| 国内精品卡一卡二卡三| 韩国精品一区二区| 男人插女人视频在线观看| 国产毛片一区二区三区| 成人激情在线观看| 久草在线中文最新视频| 日韩中文字幕在线看| 国产1区在线观看| 91成人网在线| 欧美黄色一区二区三区| 久久久久国产精品麻豆| 性折磨bdsm欧美激情另类| 久久黄色网页| 超薄肉色丝袜足j调教99| 男男gay无套免费视频欧美| 91欧美精品成人综合在线观看| 日本中文字幕在线播放| 亚洲成人av中文字幕| 欧美另类高清videos的特点| 亚洲一级片在线观看| 亚洲欧美va天堂人熟伦| 高清在线观看日韩| 亚洲老女人av| 在线观看亚洲| 综合视频免费看| 国产成人3p视频免费观看| 国产精品传媒毛片三区| 日本一区二区三区中文字幕| 欧美一区二三区| 污污片在线免费视频| 中文字幕视频一区二区在线有码 | 欧美一区二区三区在线| 青青青国产在线 | 四虎永久免费在线观看| 国产麻豆日韩欧美久久| 国产男女激情视频| 亚洲毛片播放| 日本精品福利视频| 欧美激情777| 欧美一区少妇| 日韩超碰人人爽人人做人人添| 国产91色在线| а√在线中文在线新版| 久久精品国产精品亚洲| 成人影院免费观看| 国产视频在线观看一区二区| 亚洲精品一区二区三区蜜桃| 日韩一区二区三区四区| 亚洲天堂网视频| 欧美最猛黑人xxxxx猛交| 亚洲不卡视频在线观看| 五月婷婷综合网| 国产精品日日夜夜| 一区二区久久久久| 澳门黄色一级片| 日韩美女精品在线| 欧美a级片免费看| 国产精品卡一卡二卡三| 国产不卡在线观看视频| 欧美国产欧美综合| 中文字幕第24页| 国产欧美一区二区三区在线老狼 | 日本三级2019| 亚洲最快最全在线视频| 青娱乐国产盛宴| 亚洲另类一区二区| 外国一级黄色片| 亚洲人成网站精品片在线观看| 免费的av网站| 99久久99久久精品免费看蜜桃| 冲田杏梨av在线| 日韩av在线发布| 国产a级片免费观看| 日韩成人dvd| 91精品无人成人www| 久久99精品国产麻豆婷婷洗澡| 分分操这里只有精品| 亚洲黄色精品| 黄在线观看网站| 丝袜亚洲另类欧美| 亚洲视频第二页| 国产一区不卡视频| 国产一级免费片| 91色乱码一区二区三区| 99久久久无码国产精品衣服| 中文字幕在线一区二区三区| 欧美视频www| 亚洲成va人在线观看| 日本视频网站在线观看| 欧美亚洲自拍偷拍| 国产农村妇女毛片精品久久| 精品少妇一区二区三区免费观看| 五月天中文字幕| 在线电影一区二区三区| 99精品国产99久久久久久97| 精品国精品国产尤物美女| 亚洲色图另类小说| 一区二区三区动漫| 99福利在线| 欧美一区三区三区高中清蜜桃| 草草在线视频| 国产极品jizzhd欧美| 日韩精品视频在线看| 久久偷窥视频| 外国成人免费视频| 无码人妻丰满熟妇区96| 美女爽到高潮91| 特级特黄刘亦菲aaa级| 国产免费久久精品| 麻豆changesxxx国产| 一本一道综合狠狠老| 国产强伦人妻毛片| 亚洲毛片在线免费观看| jizz性欧美10| 日韩美女主播视频| 试看120秒一区二区三区| 免费成人在线观看av| 一区二区三区四区日韩| 国产黄色特级片| 国产高清久久久| 久久久视频6r| 亚洲成人av资源| 国产一区二区三区四区视频| 日韩久久午夜影院| 特级毛片在线| 国产精品永久免费在线| 日韩av三区| 国产精品igao激情视频| 免费高清在线一区| 亚洲精品理论片| 亚洲国产毛片aaaaa无费看| 亚洲天堂中文字幕在线| 亚洲欧美日韩一区二区在线| 美足av综合网| 国产欧美精品一区二区| 九九久久婷婷| 国产主播自拍av| 国产一区二区免费视频| 手机看片国产日韩| 在线免费av一区| 日韩porn| 午夜精品一区二区三区在线| 精品国产不卡一区二区| 亚洲精品中字| 日本免费在线视频不卡一不卡二| 中文字幕永久有效| 国产香蕉久久精品综合网| 亚洲国产精一区二区三区性色| 色狠狠一区二区| 五月婷在线视频| 久久免费在线观看| 综合伊人久久| 妞干网视频在线观看| 六月丁香婷婷色狠狠久久| 蜜桃av乱码一区二区三区| 欧美性生交xxxxx久久久| 午夜性色福利视频| 欧美中文字幕在线播放| 欧美日韩一区二区三区在线电影 | 99久久免费国产| 豆国产97在线 | 亚洲| 欧美成人精品3d动漫h| 伊人在我在线看导航| 51精品国产人成在线观看| 香蕉国产精品| 午夜剧场在线免费观看| 中文字幕日韩精品一区| 91在线公开视频| 久久深夜福利免费观看| 99精品美女视频在线观看热舞| 国产一区二区三区无遮挡| 在线综合亚洲| aaaaa一级片| 一本大道av伊人久久综合| 久草视频在线看| 国产精品久久久久高潮| 水蜜桃久久夜色精品一区| 在线免费观看av网| 一区二区三区中文字幕| 蜜桃av鲁一鲁一鲁一鲁俄罗斯的| 综合激情国产一区| 亚洲一区二区av| 午夜久久久久久久久久久| 丁香六月综合激情| 可以免费看的av毛片| 一区二区三区高清国产| 国产精品视频首页| 人妻久久久一区二区三区| 久久久久国产精品麻豆| 一区二区久久精品66国产精品| 亚洲片在线观看| 欧美特黄色片| 人妻互换免费中文字幕| 91小视频在线| 最近中文字幕在线观看视频| 久久手机精品视频| 牛牛影视久久网| 五月婷婷丁香色| 亚洲一区二区三区四区在线观看 | 日韩区在线观看| jizz一区二区三区| 日本视频精品一区| 国产一二精品视频| 天天插天天操天天干| 中文字幕无线精品亚洲乱码一区 | 大黑人交xxx极品hd| 欧美亚洲国产怡红院影院| 在线观看中文| 欧美一卡2卡3卡4卡无卡免费观看水多多|