12. TypeScript 高級類型

TypeScript 中的高級類型包括映射類型、條件類型、字面量類型和遞歸類型等強大結構。這些特性使開發者能夠表達類型之間更復雜的關系,從而處理邊緣情況,并定義更動態、更靈活的類型系統。

一、映射類型

TypeScript 映射類型(Mapped Types)是一種高級類型工具,它允許我們基于已有的類型創建新的類型。通過遍歷已有類型的鍵(key),并對其進行變換,可以快速構造具有相同結構但屬性類型不同的新類型,從而提高代碼的靈活性和復用性。

(一) 概念

TypeScript 中的映射類型(Mapped Types)允許你通過轉換現有類型的屬性來創建新的類型。

  • 它們支持對屬性進行修改,例如將屬性設為可選(optional)、只讀(read-only),或者改變屬性的類型。
  • 映射類型有助于減少重復代碼,并通過自動化的類型轉換提升類型安全性。
  • 它們特別適用于創建現有類型的不同變體,而無需手動重新定義每個屬性。

例如下面的代碼所示:

type User = {id: number;name: string;email: string;
};type PartialUser = {[P in keyof User]?: User[P];
};

代碼解釋:

  • PartialUser 是一個新類型,其中 User 類型的每個屬性都被標記為可選(optional)。
  • keyof 操作符用于獲取 User 類型的所有屬性鍵,而映射類型則會遍歷每一個鍵,并通過 ? 將它們標記為可選屬性。

輸出:

const user1: PartialUser = { id: 1 }; 
const user2: PartialUser = {}; 
const user3: PartialUser = { id: 2, name: "Alice" }; 

(二) 場景示例

1. 創建只讀屬性

在 TypeScript 中,可以使用 映射類型(Mapped Types)readonly 關鍵字創建一個新的類型,使其所有屬性變為只讀。

type User = {id: number;name: string;email: string;
};type ReadonlyUser = {readonly [P in keyof User]: User[P];
};const user: ReadonlyUser = { id: 1, name: "Alice", email: "alice@example.com" };
user.id = 2;

代碼解釋:

  • ReadonlyUser 是一個新類型,其中 User 的所有屬性都被標記為只讀(readonly)。
  • 嘗試修改 user 對象的任何屬性都會導致編譯時錯誤。

輸出:

Error: Cannot assign to 'id' because it is a read-only property.

2. 創建可為空屬性

創建可為空屬性(Creating Nullable Properties)指的是將一個類型中的所有屬性變為可以為 null 的類型。在 TypeScript 中,可以使用映射類型(Mapped Types)結合聯合類型(Union Types)來實現這一點。

type Product = {name: string;price: number;inStock: boolean;
};type NullableProduct = {[P in keyof Product]: Product[P] | null;
};const product: NullableProduct = { name: "Laptop", price: null, inStock: true };

代碼解釋:

  • NullableProduct 是一個新的類型,它使得 Product 類型中的每個屬性都可以是其原始類型,或者是 null
  • 這允許屬性顯式地具有 null 值,用于表示該屬性當前沒有值或值缺失的情況。

輸出:

{ name: "Laptop", price: null, inStock: true }

3. 使用模板字面量重命名屬性

使用模板字面量重命名屬性(Renaming Properties with Template Literals)是 TypeScript 中映射類型的一種高級用法。它允許我們通過字符串模板語法在創建新類型時動態地改變屬性名。

type Person = {firstName: string;lastName: string;
};type PrefixedPerson = {[P in keyof Person as `person${Capitalize<P>}`]: Person[P];
};const person: PrefixedPerson = { personFirstName: "Felix", personLastName: "Raink" };

代碼解釋:

  • PrefixedPerson 創建了一個新類型,通過在 Person 類型的每個屬性名前加上 "person" 前綴,并將原屬性名首字母大寫。
  • 這個示例演示了如何結合模板字面量類型和 TypeScript 內置的 Capitalize 工具類型來轉換屬性名稱。

輸出:

{ personFirstName: "Felix", personLastName: "Raink" }

(三) TypeScript 映射類型使用最佳實踐

  • 保持轉換簡單:避免過于復雜的嵌套轉換,以保持代碼的可讀性和維護的便捷性。
  • 確保類型安全:利用映射類型強制執行一致的屬性轉換,提高整個代碼庫的類型安全性。
  • 結合內置工具類型使用:配合 Partial、Readonly、Pick 和 Omit 等內置工具類型,簡化常見的類型轉換操作。

二、條件類型

在 TypeScript 中,條件類型使開發者能夠根據條件創建類型,從而實現更動態和靈活的類型定義。

它們遵循語法 T extends U ? X : Y,意思是如果類型 T 可賦值給類型 U,則類型解析為 X;否則解析為 Y

(一) 概念

條件類型在創建工具類型和進行高級類型操作時特別有用,能夠增強代碼的復用性和類型安全性。

比如下面這個例子:

type IsString<T> = T extends string ? 'Yes' : 'No';type Test1 = IsString<string>;
type Test2 = IsString<number>;console.log('Test1:', 'Yes');
console.log('Test2:', 'No');
  • 類型別名 IsString 使用條件類型來判斷類型 T 是否繼承自 string
  • 如果 T 可以賦值給 string,則結果為 'Yes';否則結果為 'No'
  • Test1 被評估為 'Yes',因為 string 繼承自 string
  • Test2 被評估為 'No',因為 number 不繼承自 string

輸出:

Test1: Yes
Test2: No

(二) 場景示例

1. 條件類型約束

條件類型約束允許在條件類型中對泛型類型進行約束,從而實現動態且精確的類型處理。

type CheckNum<T> = T extends number ? T : never;type NumbersOnly<T extends any[]> = {[K in keyof T]: CheckNum<T[K]>;
};const num: NumbersOnly<[4, 5, 6, 8]> = [4, 5, 6, 8];
const invalid: NumbersOnly<[4, 6, "7"]> = [4, 6, "7"];

代碼解釋:

  • CheckNum<T> 確保只保留數字類型;其他類型則解析為 never
  • NumbersOnly<T> 對數組中的每個元素應用 CheckNum,過濾掉非數字類型。

輸出:

Type '"7"' is not assignable to type 'never'.

2. 條件類型中的類型推斷

此特性允許在條件類型定義中提取并使用類型,從而實現精確的類型轉換。

type ElementType<T> = T extends (infer U)[] ? U : never;const numbers: number[] = [1, 2, 3];
const element: ElementType<typeof numbers> = numbers[0];
const invalidElement: ElementType<typeof numbers> = "string";

代碼解釋:

  • ElementType<T> 使用 infer 關鍵字從數組中提取元素類型。
  • 變量 element 被正確推斷為 number;嘗試賦值為字符串則無效。

輸出:

Type 'string' is not assignable to type 'number'.

3. 分布式條件類型

分布式條件類型(Distributive Conditional Types)是 TypeScript 條件類型的一種特性,當條件類型作用于聯合類型時,會將條件應用到聯合類型的每個成員上,然后將結果合并成新的聯合類型。

type Colors = 'red' | 'blue' | 'green';type ColorClassMap = {red: 'danger';blue: 'primary';green: 'success';
};type MapColorsToClasses<T extends string> = T extends keyof ColorClassMap? { [K in T]: ColorClassMap[T] }: never;const redClass: MapColorsToClasses<Colors> = { red: 'danger' };
const invalidClass: MapColorsToClasses<Colors> = { yellow: 'warning' };

代碼解釋:

  • MapColorsToClasses<T> 會檢查類型 T 是否匹配 ColorClassMap 中的某個鍵,并將其映射為對應的值。
  • 'yellow' 這樣無效的顏色會被拒絕,因為它不在 ColorClassMap 中定義。

輸出:

Type '{ yellow: "warning"; }' is not assignable to type 'never'.

(三) TypeScript 條件類型的最佳實踐

  • 使用條件類型創建靈活且可復用的類型定義。
  • 將條件類型與泛型結合使用,以增強適應性。
  • 在復雜場景中利用 infer 關鍵字實現類型推斷。

三、字面量類型

TypeScript 的字面量類型允許開發者為變量、函數參數或屬性指定精確的值,通過確保變量只能持有預定義的值來增強類型安全性。

  • 允許變量具有特定且精確的值。
  • 通過限制允許的值范圍,提高代碼的可靠性。

以下是 TypeScript 中字面量類型的幾種類型:

(一) 字符串字面量類型

字符串字面量類型允許變量只接受特定的一組字符串值。

type Direction = "Up" | "Down" | "Left" | "Right";let move: Direction;move = "Up"; // 有效賦值
// move = "Forward"; // 錯誤:類型 '"Forward"' 不能賦值給類型 'Direction'
  • Direction 類型只能是指定的字符串字面量之一:“Up”、“Down”、“Left”或“Right”。
  • 賦值為該集合之外的任何值都會導致編譯時錯誤。

(二) 數字字面量類型

數字字面量類型限制變量只能取特定的一組數值。

type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;function rollDice(): DiceRoll {return 4; // 有效的返回值// return 7; // 錯誤:類型 '7' 不能賦值給類型 'DiceRoll'
}
  • DiceRoll 類型只能是 1 到 6 之間的數字之一。
  • 返回任何不在該范圍內的值都會導致編譯錯誤。

(三) 布爾字面量類型

布爾字面量類型限制變量只能是布爾值 true 或 false。

type Success = true;function operation(): Success {return true; // 合法的返回值// return false; // 錯誤:類型 'false' 不能賦值給類型 'true'
}
  • Success 類型嚴格限定為 true,返回 false 會導致編譯時錯誤。

(四) TypeScript 字面量類型的最佳實踐

  • 使用字面量類型指定精確值:定義變量時使用字面量類型,將其限制為特定的預設值,從而提升代碼的可預測性。
  • 結合聯合類型使用:利用聯合類型讓變量能接受有限的多個字面量值,增強類型安全性。
  • 利用類型別名:為復雜的字面量類型組合創建類型別名,簡化代碼結構并提升可讀性。

四、模板字面量類型

TypeScript 中的模板字面量類型允許通過使用模板字面量語法,將已有的字符串字面量類型組合,構造出新的字符串字面量類型。

(一) 概念

它們支持通過在模板字符串中嵌入聯合類型或其他字面量類型,創建復雜的字符串模式。
該特性提升了類型安全性,使開發者可以在類型層面定義并強制執行特定的字符串格式。

示例代碼:

type Size = "small" | "medium" | "large";
type SizeMessage = `The selected size is ${Size}.`;let message: SizeMessage;message = "The selected size is small.";  // 有效
message = "The selected size is extra-large.";  // 報錯

代碼解釋:

  • Size 是一個聯合類型,表示可能的尺寸值。
  • SizeMessage 是一個模板字面量類型,通過嵌入 Size,構造出具體的字符串模式。
  • 變量 message 只能被賦值為符合 SizeMessage 模式的字符串。

錯誤信息示例:

Type '"The selected size is extra-large."' is not assignable to type 'SizeMessage'.

(二) 場景示例

1. 使用 TypeScript 字面量定義路徑

type ApiEndpoints = "users" | "posts" | "comments";
type ApiPath = `/api/${ApiEndpoints}`;const userPath: ApiPath = "/api/users";
const invalidPath: ApiPath = "/api/unknown";
  • ApiEndpoints 是一個聯合類型,表示可能的 API 端點名稱。
  • ApiPath 是一個模板字面量類型,動態構造出以 /api/ 開頭,后接 ApiEndpoints 中任意值的字符串模式。
  • userPath 是有效的,因為它符合構造的模式;而 invalidPath 會報錯。

錯誤信息示例:

Type '"/api/unknown"' is not assignable to type 'ApiPath'.

2. 使用模板字面量格式化消息

type Status = "success" | "error" | "loading";
type StatusMessage = `The operation is ${Status}.`;const successMessage: StatusMessage = "The operation is success.";
const invalidMessage: StatusMessage = "The operation is pending.";
  • Status 是一個聯合類型,表示操作的可能狀態。
  • StatusMessage 構造字符串模式,用于描述操作狀態。
  • successMessage 是有效的,因為它符合模式;而 invalidMessage 報錯,因為 "pending" 不屬于 Status 類型。

錯誤信息示例:

Type '"The operation is pending."' is not assignable to type 'StatusMessage'.

五、遞歸類型

TypeScript 為 JavaScript 添加了強類型支持。遞歸類型定義了可以自我引用的類型,適用于樹形結構或嵌套對象。實用工具類型(Utility Types)則簡化了類型的修改,比如將屬性設為可選或只讀。這些工具幫助我們寫出更清晰、更靈活的代碼。

(一) TypeScript 中的遞歸類型

遞歸類型是在其定義中引用自身的類型。這使得我們能夠建模復雜的數據結構,比如樹、鏈表和嵌套對象,其中類型可以嵌套自身。

  • 允許定義自我引用的類型。
  • 適合表示層級或嵌套數據。
  • 必須使用 TypeScript 的類型別名(type alias)或接口(interface)來定義遞歸結構。

1. 語法示例

下面是一個表示樹結構的簡單遞歸類型:

type TreeNode = {value: number;children?: TreeNode[];
};
  • TreeNode 類型包含一個 value 屬性和一個可選的 children 屬性。
  • children 是一個 TreeNode 數組,支持嵌套結構。

2. 使用遞歸類型示例

const tree: TreeNode = {value: 1,children: [{ value: 2 },{value: 3,children: [{ value: 4 },{ value: 5 }]}]
};

在此示例中,tree 表示一個層級結構,節點可以有子節點,子節點又可以有自己的子節點,依此類推。

3. 遞歸類型的優點

  • 建模復雜結構:遞歸類型方便表示層級結構。
  • 類型安全:TypeScript 確保遞歸結構在每個嵌套層級都符合正確的類型。
  • 類型復用:遞歸類型可以在多種場景中復用相同的結構定義。

(二) TypeScript 中的實用工具類型(Utility Types)

TypeScript 內置了一些實用工具類型,提供了修改或轉換其他類型的現成功能,簡化常見的類型操作,提高開發效率。

1. 常見實用工具類型

Partial<T>

將類型 T 的所有屬性設為可選。
適用于創建部分屬性可缺失的對象。

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

將類型 T 的所有屬性設為必需。
確保對象中的所有屬性必須存在。

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

將類型 T 的所有屬性設為只讀。
防止初始化后修改對象屬性。

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

Pick<T, K>

從類型 T 中挑選出屬性 K 的子集。
適用于從復雜類型中選取部分屬性。

type Pick<T, K extends keyof T> = {[P in K]: T[P];
};

Omit<T, K>

從類型 T 中剔除屬性 K。
用于排除不需要的屬性。

type Omit<T, K extends keyof T> = {[P in Exclude<keyof T, K>]: T[P];
};

Record<K, T>

構造一個對象類型,其屬性鍵為 K,值為 T。
用于定義類似映射的數據結構。

type Record<K extends keyof any, T> = {[P in K]: T;
};

Exclude<T, U>

從類型 T 中排除可賦值給 U 的類型。
用于過濾聯合類型中的部分類型。

type Exclude<T, U> = T extends U ? never : T;

Extract<T, U>

從類型 T 中提取可賦值給 U 的類型。
用于縮小聯合類型范圍。

type Extract<T, U> = T extends U ? T : never;

NonNullable<T>

從類型 T 中排除 nullundefined
確保值不是 null 或 undefined。

type NonNullable<T> = T extends null | undefined ? never : T;

2. 實用工具類型的優點

  • 簡化類型轉換:內置的工具類型方便對類型結構進行變換(如變成可選、必需等)。
  • 提升代碼可讀性:使用簡潔的類型關鍵字表達轉換意圖,使代碼更清晰。
  • 提高開發效率:避免重復手寫復雜類型定義,減少錯誤。

通過遞歸類型和實用工具類型,TypeScript 為復雜數據結構和類型操作提供了強大而靈活的支持,助力開發者編寫更安全、更高效的代碼。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/910516.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/910516.shtml
英文地址,請注明出處:http://en.pswp.cn/news/910516.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

韓國證券交易所(KRX)全生態接入系統技術白皮書

核心價值&#xff1a;為全球最活躍的衍生品市場&#xff08;日均交易量480億美元&#xff09;提供 5μs延遲引擎全合規認證&#xff0c;助力中資機構搶占韓國78%衍生品交易份額 一、KRX市場機遇與準入壁壘 1.1 核心數據錨定&#xff08;2025Q2&#xff09; 指標數值全球競爭力…

【Clickhouse系列】增刪改查:對比mysql

目錄 1. 寫入操作 (INSERT) 2. 刪除操作 (DELETE) 3. 更新操作 (UPDATE) 4. 查詢操作 (SELECT) 5. 總結對比表&#xff1a; 6. 參考鏈接 核心哲學差異&#xff1a; MySQL&#xff1a; 面向在線事務處理。核心目標是保證數據的強一致性、原子性和低延遲的單行操作&#x…

低壓電工作業中,如何正確選用熔斷器的額定電流?

在低壓電工作業中&#xff0c;正確選用熔斷器額定電流需綜合考慮負載類型、額定電流等因素&#xff0c;具體方法如下&#xff1a; 照明電路&#xff1a;對于白熾燈負載&#xff0c;熔體額定電流可按被保護電路上所有白熾燈工作電流之和的 1.1 倍選取。若是日光燈和高壓水銀熒…

MySQL:索引優化實戰技巧

目錄 一、前言 二、基礎知識回顧 三、索引設計優化 1.遵循最左匹配原則&#xff0c;合理設計聯合索引順序 2.利用覆蓋索引避免回表查詢 3.針對字符串列使用前綴索引 4.合理使用復合索引替代多個單列索引 5.使用前綴索引優化模糊查詢的左匹配 四、索引使用優化 1.避免在…

開關電源計算輔助軟件SMPSKIT V10.3

資料下載地址&#xff1a;開關電源計算輔助軟件SMPSKIT V10.3 SMPSKIT &#xff1a; 內置一些常見IC的計算 內置絕大多數磁芯數據 內置變壓器分層計算器 可用戶編程功能 包含絕大多數拓…

OpenHarmony應用開發-全量包的使用

文章目錄 一、下載full-sdk二、替換本地對應版本的SDK1.查看本地SDK安裝目錄2.替換對應的SDK版本 三、升級APL權限為系統權限&#xff08;升級后便可使用系統接口&#xff09;四、重啟IDE并重新進行應用簽名總結 一、下載full-sdk 可以在官方提供的“每日構建”中搜索對應版本…

sudo安裝pip包的影響

使用 sudo 安裝的 pip 包和不使用 sudo 安裝的 pip 包在 Ubuntu 20.04 上有以下幾個主要區別&#xff1a; 1. 安裝位置&#xff1a; 使用 sudo: 包會被安裝到系統級別的 Python 環境中&#xff0c;通常是 /usr/local/lib/python3.8/dist-packages/ 或 /usr/lib/python3/dist-…

uniapp 多圖上傳,加水印功能(全平臺通用)

多圖上傳和水印都是比較難得&#xff0c;特別是有的api只支持在小程序用&#xff0c;h5不給用 效果圖 普通的多圖上傳 // 多圖上傳 // count&#xff1a;最大數量 export function headerUploads0(count 9, orderNumber , watermarkInfo) {return new Promise((resolve, r…

【appium】5. Appium WebDriver 支持的常用方法匯總

下面是一個完整的 Appium WebDriver 支持的常用方法匯總&#xff0c;并附上典型用法示例。 一、元素查找方法/元素操作方法 ? 使用 find_element() 和 find_elements() from appium import webdriver from appium.webdriver.common.appiumby import AppiumBy# 單個元素查找 …

FreeRTOS 介紹、使用方法及應用場景

一、FreeRTOS 概述 FreeRTOS 是一款廣泛應用于嵌入式系統的實時操作系統&#xff08;RTOS&#xff09;&#xff0c;具有開源、可移植、可裁剪、輕量級等顯著特點。它最初由 Richard Barry 開發&#xff0c;如今已成為全球開發者在物聯網、工業控制、消費電子等領域的熱門選擇&a…

深度解析 Caffeine:高性能 Java 緩存庫

1. Caffeine 簡介 Caffeine 是一個基于 Java 8 的高性能本地緩存庫&#xff0c;由 Ben Manes 開發&#xff0c;旨在替代 Google Guava Cache&#xff0c;提供更優的緩存策略、更高的吞吐量和更靈活的配置。 核心優勢 ? 卓越的性能&#xff1a;采用優化的數據結構&#xff0…

創客匠人賦能創始人 IP 打造:健康行業知識變現案例深度解析

在知識服務行業蓬勃發展的當下&#xff0c;創始人 IP 打造已成為知識變現的核心驅動力。創客匠人近期披露的陪跑案例顯示&#xff0c;通過系統化的線上線下聯動運營&#xff0c;傳統行業從業者可高效實現 IP 價值轉化。以亓黃中醫科技創始人吳豐言老師為例&#xff0c;其在創客…

64、最小路徑和

題目&#xff1a; 解答&#xff1a; 簡單dp。 定義&#xff1a;dp[i][j]為到達(i,j)所需要的最短路程 初始化&#xff1a;dp[0][0]grid[0][0]&#xff0c;同時對第一行和第一列的&#xff0c;第i個就是前i個之和加上自身 遞歸&#xff1a;dp[i][j]min(dp[i-1][j],dp[i][j-1…

獲取連接通義千問大語言模型配置信息的步驟:api_key、api_url

一、注冊并開通通義千問API服務 1. 注冊阿里云賬號 訪問 阿里云官網點擊右上角"免費注冊"&#xff0c;按指引完成賬號注冊和實名認證 2. 開通通義千問API服務 進入 通義千問API產品頁點擊"立即開通"&#xff0c;按提示完成服務開通&#xff08;部分服務…

汽車加氣站操作工考試題庫含答案【最新】

1.天然氣的主要成分是&#xff08;&#xff09;。 A. 乙烷 B. 乙烯 C. 甲烷 D. 乙炔 答案&#xff1a;C 2.CNG 加氣站中&#xff0c;加氣機的加氣軟管應&#xff08;&#xff09;進行檢查。 A. 每天 B. 每周 C. 每月 D. 每季度 答案&#xff1a;A 3.儲氣罐的安全閥應&#xf…

顯示任何結構的數組對象數據【向上自動滾動】

顯示任何結構的數組對象數據 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>地圖編輯軟件 - 數…

GPIO模式詳解

一、GPIO的八種模式 GPIO支持4種輸入模式&#xff08;浮空輸入、上拉輸入、下拉輸入、模擬輸入&#xff09;和4種輸出模式&#xff08;開漏輸出、開漏復用輸出、推挽輸出、推挽復用輸出&#xff09;。 GPIO_Mode_AIN模擬輸入GPIO_Mode_IN_FLOATING浮空輸入GPIO_Mode_IPD下拉輸…

django rest_framework 自定義403 Forbidden錯誤頁面

django本來有是可以很方便自定義HTTP錯誤頁面的&#xff0c;網上資料一大把。核心是在項目的urls代碼中增加handler403的定義&#xff0c;比如&#xff1a; handler403 "app.views.your_custom_view" 404&#xff0c;500都是一樣的&#xff0c;重新定義handler404…

Kafka Streams架構深度解析:從并行處理到容錯機制的全鏈路實踐

在流處理技術領域&#xff0c;Kafka Streams以其輕量級架構與Kafka生態的深度整合能力脫穎而出。作為構建在Kafka生產者/消費者庫之上的流處理框架&#xff0c;它通過利用Kafka原生的分區、副本與協調機制&#xff0c;實現了數據并行處理、分布式協調與容錯能力的無縫集成。本文…

【嵌入式硬件實例】-555定時器控制舵機/伺服電機

555定時器控制舵機/伺服電機 文章目錄 555定時器控制舵機/伺服電機1、555定時器介紹2、舵機/伺服電機介紹3、硬件準備與接線使用 555 定時器 IC 的伺服電機控制器和測試儀電路是一個簡單的電路,可用于生成操作伺服電機所需的控制信號。該電路允許我們通過按下按鈕手動驅動/控制…