TypeScript 泛型講解

如果說 TypeScript 是一門對類型進行編程的語言,那么泛型就是這門語言里的(函數)參數。本章,我將會從多角度講解?TypeScript 中無處不在的泛型,以及它在類型別名、對象類型、函數與 Class 中的使用方式。

一、泛型的核心概念

1.基本定義

泛型(Generics)是 TypeScript 中允許在定義函數、接口或類時不預先指定具體類型,而是在使用時動態指定類型的機制。其核心目標是實現代碼的可重用性類型安全

  • 示例
    function identity<T>(arg: T): T { return arg; }

    此處?<T>?為類型參數,T?在調用時被具體類型替換,如?identity<string>("hello")

  • 不過上述例子中直接??identity("hello") 也是可以的,省略不寫類型參數的值,讓 TypeScript 自己推斷。但有些復雜的使用場景,TypeScript 可能推斷不出類型參數的值,這時就必須顯式給出了。

    function comb<T>(arr1:T[], arr2:T[]):T[] {return arr1.concat(arr2);
    }comb([1, 2], ['a', 'b']) // 報錯comb<number|string>([1, 2], ['a', 'b']) // 正確

    上面示例中,兩個參數arr1arr2和返回值都是同一個類型。如果不給出類型參數的值,調用會報錯。如果類型參數是一個聯合類型,就不會報錯。

2.?泛型 vs?any

  • any?的缺陷:放棄類型檢查,失去 TypeScript 的類型安全優勢。
  • 泛型的優勢:保留類型信息,編譯器可進行靜態檢查,如類型推斷與錯誤提示 。

二、泛型的主要應用場景

泛型主要用在四個場合:函數、接口、類和別名。

1. 函數的泛型寫法

通過泛型定義可處理多種類型的函數,避免重復代碼:

function reverse<T>(items: T[]): T[] {return items.reverse();
}
const numbers = reverse([1, 2, 3]); // 推斷為 number[]
const strings = reverse(["a", "b"]); // 推斷為 string[]

此例中,T 自動匹配輸入數組類型,返回值類型與輸入一致。

2.?接口的泛型寫法

定義靈活的類型契約,適用于容器類場景:

interface KeyValuePair<K, V> {key: K;value: V;
}
const pair: KeyValuePair<number, string> = { key: 1, value: "one" };

接口通過類型參數 KV 支持多種鍵值組合。

3. 類的泛型寫法

創建可復用的數據結構(如集合、棧、隊列):

class Stack<T> {private items: T[] = [];push(item: T) { this.items.push(item); }pop(): T | undefined { return this.items.pop(); }
}
const numberStack = new Stack<number>();
numberStack.push(42); // 僅允許 number 類型

此類實現保證了棧內元素的類型一致性。

4. 類型別名的泛型寫法

type 命令定義的類型別名,也可以使用泛型。

type Nullable<T> = T | undefined | null;

上面示例中,Nullable<T>是一個泛型,只要傳入一個類型,就可以得到這個類型與undefinednull的一個聯合類型。

三、高級泛型技巧

1. 泛型約束

通過 extends 限制類型參數的范圍:

interface HasLength { length: number; }
function logLength<T extends HasLength>(arg: T): void {console.log(arg.length);
}
logLength("hello"); // 合法(length=5)
logLength(42);      // 錯誤:缺少 length 屬性

此約束確保類型參數必須包含指定屬性。

2. 多類型參數與默認值

多類型參數

function swap<T, U>(tuple: [T, U]): [U, T] {return [tuple[1], tuple[0]];
}
swap([7, "seven"]); // 返回 ["seven", 7]

但是類型參數越少越好,下面我會講到

默認類型

class Generic<T = string> {list:T[] = []add(t:T) {this.list.push(t)}
}const g = new Generic();
g.add(4) // 報錯
g.add('hello') // 正確-------------------------------------------const g = new Generic<number>();
g.add(4) // 正確
g.add('hello') // 報錯

上面示例中,類Generic有一個類型參數T,默認值為string。這意味著,屬性list默認是一個字符串數組,方法add()的默認參數是一個字符串。所以,向add()方法傳入一個數值會報錯,傳入字符串就不會。反之,傳入字符串會報錯。

3.?索引類型與?keyof

確保對象屬性訪問的安全性:

function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {return obj[key];
}
const person = { name: "Alice", age: 30 };
getValue(person, "name"); // 合法
getValue(person, "gender"); // 錯誤:屬性不存在。

4.?條件類型與映射類型

條件類型:根據條件選擇類型:

type Check<T> = T extends string ? "string" : "not string";
type A = Check<"hello">; // "string"

映射類型:基于已有類型生成新類型:

type Readonly<T> = { readonly [P in keyof T]: T[P] };
type ReadonlyPerson = Readonly<Person>; // 所有屬性變為只讀。

四、泛型的正確使用場景與注意點

1.正確使用場景

  • 當需要在多個位置(參數、返回值、成員變量)之間建立類型約束時。
  • 避免重復編寫相似邏輯的類型特定代碼(如不同數據類型的隊列實現)。

2.注意點

1、盡量少用泛型。

泛型雖然靈活,但是會加大代碼的復雜性,使其變得難讀難寫。一般來說,只要使用了泛型,類型聲明通常都不太易讀,容易寫得很復雜。因此,可以不用泛型就不要用。

2、類型參數越少越好。

多一個類型參數,多一道替換步驟,加大復雜性。因此,類型參數越少越好。

function filter<T,Fn extends (arg:T) => boolean
>(arr:T[],func:Fn
): T[] {return arr.filter(func);
}

上面示例有兩個類型參數,但是第二個類型參數?Fn?是不必要的,完全可以直接寫在函數參數的類型聲明里面。

function filter<T>(arr:T[],func:(arg:T) => boolean
): T[] {return arr.filter(func);
}

上面示例中,類型參數簡化成了一個,效果與前一個示例是一樣的。

3、類型參數需要出現兩次。

如果類型參數在定義后只出現一次,那么很可能是不必要的。

function greet<Str extends string>(s:Str
) {console.log('Hello, ' + s);
}

上面示例中,類型參數Str只在函數聲明中出現一次(除了它的定義部分),這往往表明這個類型參數是不必要。

function greet(s:string) {console.log('Hello, ' + s);
}

上面示例把前面的類型參數省略了,效果與前一個示例是一樣的。

也就是說,只有當類型參數用到兩次或兩次以上,才是泛型的適用場合。

4、泛型可以嵌套。

類型參數可以是另一個泛型。

type OrNull<Type> = Type|null;
type OneOrMany<Type> = Type|Type[];
type OneOrManyOrNull<Type> = OrNull<OneOrMany<Type>>;

上面示例中,最后一行的泛型OrNull的類型參數,就是另一個泛型OneOrMany

五、實戰應用案例

1.?React 組件泛型

定義可接收多種 props 類型的組件:

interface ListProps<T> {items: T[];renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {return <div>{items.map(renderItem)}</div>;
}
// 使用
<List<number> items={[1, 2]} renderItem={(n) => <div>{n}</div>} />。

2.?API 請求封裝

利用泛型約束返回數據類型:

async function fetchData<T>(url: string): Promise<T> {const response = await fetch(url);return response.json() as T;
}
interface User { id: number; name: string; }
const users = await fetchData<User[]>("/api/users");。

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

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

相關文章

SQL 每日一題(6)

繼續做題&#xff01; 原始表&#xff1a;employee_resignations表 employee_idresignation_date10012022-03-1510022022-11-2010032023-01-0510042023-07-1210052024-02-28 第一題&#xff1a; 查詢累計到每個年度的離職人數 結果輸出&#xff1a;年度、當年離職人數、累計…

工業RTOS生態重構:從PLC到“端 - 邊 - 云”協同調度

一、引言 在當今數字化浪潮席卷全球的背景下&#xff0c;工業領域正經歷著深刻變革。工業自動化作為制造業發展的基石&#xff0c;其技術架構的演進直接關系到生產效率、產品質量以及企業的市場競爭力。傳統的PLC&#xff08;可編程邏輯控制器&#xff09;架構雖然在工業控制領…

從版本控制到協同開發:深度解析 Git、SVN 及現代工具鏈

前言&#xff1a;在當今軟件開發的浪潮中&#xff0c;版本控制與協同開發無疑扮演著舉足輕重的角色。從最初的單兵作戰到如今大規模團隊的高效協作&#xff0c;一套成熟且得力的版本控制系統以及圍繞其構建的現代工具鏈&#xff0c;已然成為推動軟件項目穩步前行的關鍵引擎。今…

Visual Studio Code插件離線安裝指南:從市場獲取并手動部署

Visual Studio Code插件離線安裝指南&#xff1a;從市場獲取并手動部署 一、場景背景二、操作步驟詳解步驟1&#xff1a;訪問官方插件市場步驟2&#xff1a;定位目標版本步驟3&#xff1a;提取關鍵參數步驟4&#xff1a;構造下載鏈接步驟5&#xff1a;下載與安裝 三、注意事項 …

用HTML5實現實時ASCII藝術攝像頭

用HTML5實現實時ASCII藝術攝像頭 項目簡介 這是一個將攝像頭畫面實時轉換為ASCII字符藝術的Web應用&#xff0c;基于HTML5和原生JavaScript實現。通過本項目可以學習到&#xff1a; 瀏覽器攝像頭API的使用Canvas圖像處理技術實時視頻流處理復雜DOM操作性能優化技巧 功能亮點…

論文審稿之我對SCI寫作的思考

有幸被邀請審過二區、三區、四區期刊的論文&#xff0c;近期審稿10余篇&#xff0c;分享一下我從一個審稿人的角度出發&#xff0c;如何提升自己寫作的質量。 作圖高清和好看&#xff0c;永遠是排第一位。圖中的字要清晰&#xff0c;有的放大200%還看不清字&#xff1b;每幅圖的…

MLA:Transformer的智能變形金剛——解密多頭潛在注意力的進化密碼

第一章 MLA的進化之路&#xff1a;從MHA到智能變形 1.1 變形金剛的誕生背景 當LLM模型規模突破萬億參數量級時&#xff0c;傳統Transformer的注意力機制開始顯現"成長的煩惱"&#xff1a;訓練階段計算密集、推理階段內存吃緊。DeepSeek團隊的MLA如同給注意力模塊裝…

電子電路:電學都有哪些核心概念?

電子是基本粒子,帶負電荷。電荷是物質的一種屬性,電子帶有負電荷,而質子帶有正電荷。電荷的單位是庫侖。 電流呢,應該是指電荷的流動,單位是安培,也就是庫侖每秒。所以電流其實就是電荷在導體中的移動形成的。比如,當電子在導線中流動時,就形成了電流。不過要注意,傳…

第三次中醫知識問答模型微調

本次參數 llamafactory-cli train \ --stage sft \ --do_train True \ --model_name_or_path /home/qhyz/zxy/LLaMA-Factory/model \ --preprocessing_num_workers 16 \ --finetuning_type lora \ --template deepseek3 \ --flash_attn fa2 \ --dataset_dir data \ --dataset …

leetcode2081. k 鏡像數字的和-hard

1 題目&#xff1a;k 鏡像數字的和 官方標定難度&#xff1a;難 一個 k 鏡像數字 指的是一個在十進制和 k 進制下從前往后讀和從后往前讀都一樣的 沒有前導 0 的 正 整數。 比方說&#xff0c;9 是一個 2 鏡像數字。9 在十進制下為 9 &#xff0c;二進制下為 1001 &#xff…

計算機網絡學習(七)——IP

一、IP 在計算機網絡中&#xff0c;IP&#xff08;Internet Protocol&#xff0c;網際協議&#xff09;是網絡層的核心協議&#xff0c;用于實現跨越不同網絡的數據包傳輸。IP 是 TCP/IP 協議族的核心部分&#xff0c;屬于網絡層協議&#xff0c;也是 Internet 賴以運作的基礎…

【技術追蹤】ADDP:通過交替去噪擴散過程學習用于圖像識別和生成的通用表示(ICLR-2024)

擴散模型交替去噪&#xff1a;助力圖像識別與圖像生成~ 論文&#xff1a;ADDP: Learning General Representations for Image Recognition and Generation with Alternating Denoising Diffusion Process 代碼&#xff1a;https://github.com/ChangyaoTian/ADDP 0、摘要 圖像識…

在Linux上安裝Miniconda

在Linux上安裝Anaconda或Miniconda&#xff08;輕量級版本&#xff09; 選擇安裝版本 Anaconda&#xff1a; 包含200預裝包&#xff08;如NumPy、Pandas、TensorFlow等&#xff09;&#xff0c;適合新手或需要完整科學計算環境的用戶。 安裝包較大&#xff08;約500MB&#xff…

SRS流媒體服務器之RTC播放環境搭建

環境概述 srs版本 commit 44f0c36b61bc7c3a1d51cb60be0ec184c840f09d Author: winlin <winlinvip.126.com> Date: Wed Aug 2 10:34:41 2023 0800Release v4.0-r5, 4.0 release5, v4.0.271, 145574 lines. rtc.conf # WebRTC streaming config for SRS. # see full.…

清山垃圾的3個問題

與一群驢友進山&#xff0c;同步撿拾一路的垃圾&#xff1a;清山行動。 關于垃圾&#xff0c;大家提了3個問題。記錄于此&#xff0c;勤于思考&#xff1a;為什么&#xff0c;如何做 問題 - 山里的垃圾有哪些&#xff1f; - 垃圾是誰丟的&#xff1f; - 他們為…

redis集合類型

練習命令使用&#xff0c;具體如下&#xff1a; 練習無序集合類型命令 sadd smembers scard srem sinter sunion sdiff sismember srandmember spop 練習有序集合類型命令 無序集合中的每個元素都是不同的&#xff0c;且沒有順序 創建/追加/刪除/查看 127.0.0.1:6379>…

JAVA 包管理

一 、關鍵點 包聲明規則&#xff1a; 每個類首行的package聲明必須與文件路徑完全匹配com.example.math對應路徑com/example/mathorg.demo.greeting對應路徑org/demo/greeting 編譯參數&#xff1a; -d ./build&#xff1a;指定編譯輸出目錄編譯器會自動根據包聲明創建對應…

Linux中的文件系統和軟硬連接

磁盤的訪問方式 CHS&#xff08;柱面&#xff0c;磁頭&#xff0c;扇區&#xff09; 法&#xff08;磁盤硬件查找&#xff09;&#xff1a; 確定柱面&#xff08;C&#xff09; 磁頭臂移動到對應的柱面位置。例如&#xff0c;柱面號為 5&#xff0c;則磁頭移動到第 5 個磁道組…

whisper相關的開源項目 (asr)

基于 Whisper&#xff08;OpenAI 的開源語音識別模型&#xff09;的開源項目有很多&#xff0c;涵蓋了不同應用場景和優化方向。以下是一些值得關注的項目&#xff1a; 1. 核心工具 & 增強版 Whisper OpenAI Whisper 由 OpenAI 開源的通用語音識別模型&#xff0c;支持多語…

深入解析Spring Boot與JUnit 5集成測試的最佳實踐

深入解析Spring Boot與JUnit 5集成測試的最佳實踐 引言 在現代軟件開發中&#xff0c;單元測試和集成測試是確保代碼質量的重要手段。Spring Boot作為當前最流行的Java Web框架之一&#xff0c;提供了豐富的測試支持。而JUnit 5作為最新的JUnit版本&#xff0c;引入了許多新特…