TypeScript 泛型入門(新手友好、完整詳解)

目標讀者:剛學 TS 的前端開發者,或希望把泛型用到實際工程(請求封裝、組件復用)中的同學。


目錄

  1. 為什么需要泛型(直觀動機)
  2. 基本語法與例子(函數、接口、類)
  3. 泛型約束(extendskeyof
  4. 進階語法:默認類型、多個類型參數、泛型推斷
  5. 實戰一:request<T> 網絡請求封裝(詳細講解)
  6. 實戰二:React 通用下拉組件 <Select<T>>(含使用示例)
  7. 常見坑、調試技巧與最佳實踐
  8. 練習題與參考資料

1. 為什么需要泛型(直觀動機)

在沒有泛型的世界里,如果你寫一個工具函數或組件只能處理單一類型,就會出現大量重復代碼或喪失類型提示。

舉例:寫一個返回第一個元素的 first 函數,如果不使用泛型,你可能寫成 any,失去類型安全:

function firstBad(arr: any[]) { return arr[0]; }
const a = firstBad([1,2,3]); // a 的類型是 any,編輯器不會提示

使用泛型后:

function first<T>(arr: T[]): T | undefined { return arr[0]; }
const a = first([1,2,3]); // a 被推斷為 number | undefined

泛型能讓工具/組件“對所有類型通用”,同時保留類型信息,這就是它的價值。


2. 基本語法與例子

2.1 泛型函數

// 最基礎的泛型函數:identity
function identity<T>(arg: T): T {return arg;
}const s = identity('hello'); // T 被推斷為 string
const n = identity<number>(123); // 顯示指定泛型

注意:一般情況下不必顯式寫 <T>,TypeScript 會根據參數自動推斷。

2.2 泛型類型別名 / 接口

type Box<T> = { value: T };
const b: Box<number> = { value: 42 };interface ApiResponse<T> {code: number;data: T;
}const r: ApiResponse<string[]> = { code: 0, data: ['a','b'] };

2.3 泛型類

class Stack<T> {private items: T[] = [];push(item: T) { this.items.push(item); }pop(): T | undefined { return this.items.pop(); }
}const s = new Stack<number>();
s.push(1);

2.4 多個類型參數

function mapArray<T, U>(arr: T[], fn: (t: T) => U): U[] {return arr.map(fn);
}const r = mapArray([1,2,3], x => x.toString()); // r: string[]

3. 泛型約束(extendskeyof

有時候我們要限制泛型的“范圍”,比如只允許對象類型、必須包含某些屬性等。

3.1 extends 限制

function pluck<T extends object, K extends keyof T>(obj: T, key: K) {return obj[key];
}const user = { id: '1', name: 'Alice' };
pluck(user, 'name'); // OK
// pluck(user, 'notExist'); // Error

解釋:K extends keyof T 表示 K 必須是 T 的鍵之一,防止傳入不存在的屬性名。

3.2 keyof 的常見用法

type KeysOfUser = keyof typeof user; // 'id' | 'name'

4. 進階語法(默認類型、泛型推斷等)

4.1 默認類型

function identityDefault<T = string>(arg: T): T { return arg; }
const a = identityDefault('x'); // T 推斷為 string

4.2 泛型推斷

TypeScript 會根據函數參數自動推斷泛型類型,像 identity([1,2,3]) 會推斷 Tnumber[] 的元素類型(… 具體依賴簽名)。


5. 實戰一:封裝 request<T>(網絡請求)

目的:寫一個簡單且實用的 request,在調用處能用泛型指定返回類型,從而獲得完整的類型提示。

5.1 需求與設計

  • 希望 request<T>(url) 返回 Promise<T>
  • 在大多數場景后端返回的是一個包裹結構,比如 { code: number, data: T },我們也要支持。
  • 稍微封裝錯誤處理與超時(示例化,不追求復雜性)。

5.2 代碼實現(utils/request.ts

// utils/request.ts
export type ApiResponse<T> = { code: number; data: T; message?: string };export async function request<T = any>(url: string, init?: RequestInit): Promise<T> {const controller = new AbortController();const timeout = setTimeout(() => controller.abort(), 10_000);try {const res = await fetch(url, { signal: controller.signal, ...init });if (!res.ok) throw new Error(res.statusText);const data = await res.json();return data as T; // 注意:這是類型斷言,運行時不會做檢查} finally {clearTimeout(timeout);}
}

5.3 使用示例

// types.ts
type User = { id: string; name: string };// 使用(直接返回數組)
const users = await request<User[]>('/api/users');
users[0].name; // 編輯器會提示 name// 使用(后端返回包裹結構)
const resp = await request<ApiResponse<User[]>>('/api/users-pkg');
const list = resp.data; // 正常使用

5.4 提醒:類型安全與運行時驗證

TypeScript 的類型只存在編譯階段。request<T> 中的 return data as T 是“信任后端返回的結構”。如果需要更嚴格的保證,請在運行時做校驗(使用 zodio-ts 等)。


6. 實戰二:React 通用下拉組件 <Select<T>>(簡單到常用)

目標:實現一個對數據類型“透明”的下拉組件,使用泛型后,父組件拿到 onChange 的回調類型時能直接獲得具體類型提示。

6.1 需求與設計

  • 組件接收 options: T[]
  • 需要 getLabel?: (item: T) => string,用于渲染文本。
  • 需要 keyExtractor?: (item: T, idx: number) => string | number,用于 keyvalue(避免假設數據有 id 字段)。
  • onChange?: (item: T | null) => void

6.2 組件代碼(簡潔、可用)

import React from 'react';export interface SelectProps<T> {options: T[];value?: T | null;onChange?: (item: T | null) => void;placeholder?: string;getLabel?: (item: T) => string;keyExtractor?: (item: T, idx: number) => string | number;
}// 注意箭頭函數組件寫法:const Select = <T,>(props: SelectProps<T>) => { ... }
export const Select = <T,>({ options, value, onChange, placeholder, getLabel, keyExtractor }: SelectProps<T>) => {const labelOf = getLabel ?? ((it: T) => String((it as any)));const keyOf = keyExtractor ?? ((_: T, idx: number) => idx);return (<selectvalue={options.indexOf(value as T)}onChange={(e) => {const idx = Number(e.target.value);onChange?.(idx >= 0 ? options[idx] : null);}}><option value={-1}>{placeholder ?? '請選擇'}</option>{options.map((it, i) => (<option key={String(keyOf(it, i))} value={i}>{labelOf(it)}</option>))}</select>);
};

說明

  • const Select = <T,>(...) 中的 ,(逗號)是一個常用寫法,用來避免 TSX 將 <T> 誤解析為 JSX;這是聲明泛型函數表達式/箭頭函數時的語法技巧。
  • 為了讓組件與任意數據結構配合,我們沒有假定 itemidlabel 字段,而是通過 keyExtractorgetLabel 注入策略。

6.3 使用示例

// App.tsx
import React, { useState, useEffect } from 'react';
import { Select } from './Select';
import { request } from './utils/request';type User = { id: string; name: string };function App() {const [users, setUsers] = useState<User[]>([]);const [sel, setSel] = useState<User | null>(null);useEffect(() => {request<User[]>('/api/users').then(setUsers).catch(console.error);}, []);return (<div><Selectoptions={users}value={sel}onChange={(u) => setSel(u)}getLabel={(u) => u.name}keyExtractor={(u) => u.id}placeholder="選擇用戶"/><div>當前選中:{sel ? sel.name : '無'}</div></div>);
}

類型體驗:當你寫 onChange={(u) => setSel(u)} 時,編輯器會推斷 u 的類型為 User | null,這給你編輯器級別的保護與提示。

6.4 關于顯式泛型(什么時候必須)

通常只要 options 的類型是具體的數組(User[]),TS 能推斷出 T,使用時不需要寫 <Select<User> />
如果推斷失敗(例如 options 類型被擦除為 any[]),你可以:

  • 在數據源處把類型寫清楚(推薦);
  • 或在組件使用處做類型斷言:<Select options={someAny as User[]} ... />

7. 常見坑、調試技巧與最佳實踐

  • 不要濫用 any:泛型的一個目標就是替代 any,保留類型信息。
  • 理解類型與運行時的邊界:泛型只是編譯期工具,運行時沒有類型檢查。
  • 在庫/公共代碼中多寫泛型,在應用層用具體類型;庫需要更強的泛型設計能力。
  • 避免過度復雜的類型:當類型系統變得難以理解時,權衡是否用運行時校驗來代替復雜類型。
  • 在 React 中盡量依賴類型推斷,不要在 JSX 里頻繁顯式寫 <Component<Type> />(有時會引起解析問題)。

8. 練習題(自測)

  1. 寫一個泛型 filterMap<T, U>,它的簽名為 (arr: T[], fn: (t: T) => U | null) => U[]
  2. 基于 request<T>,寫一個 getJson<T>(url),當后端返回 { code, data } 結構時,自動返回 data
  3. 修改 Select 組件,使它支持 multiple(多選)并確保類型安全。

9. 總結與下一步學習建議

  • 泛型讓你的代碼既通用類型安全,是編寫可復用工具與組件的核心。
  • 推薦掌握:泛型約束(extends)、keyof、條件類型(下一步,可學 infer)、以及常見內置工具類型(Partial/Readonly/Record)。

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

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

相關文章

Linux ARP老化機制/探測機制/ip neigh使用

文章目錄1. ARP狀態機1.1 ARP狀態類型1.2 狀態轉換圖2. 超時時間與參數2.1 主要超時參數2.1.1 基礎時間參數2.1.2 探測相關參數2.1.3 垃圾回收參數3. 主機發送ARP報文的時機3.1 發送數據包時發現ARP緩存中沒有目標IP的MAC地址3.2 ARP條目進入STALE狀態后需要發送數據3.3 定期維…

便攜式顯示器怎么選?:6大關鍵指標全解析

地鐵通勤路上&#xff0c;程序員小陳掏出背包里的便攜屏&#xff0c;連接手機即刻擴展出第二個工作窗口&#xff0c;趕在上班前修復了緊急bug&#xff1b;咖啡廳里&#xff0c;設計師阿琳用筆記本加便攜屏的雙屏組合&#xff0c;一邊參看客戶brief一邊修改方案&#xff0c;效率…

以太坊網絡

以太坊的網絡和以太網是兩個完全不同的概念&#xff0c;雖然它們的名稱聽起來相似&#xff0c;但它們屬于不同的技術領域。以太坊網絡&#xff08;Ethereum Network&#xff09;領域&#xff1a;區塊鏈和加密貨幣定義&#xff1a;以太坊是一個開源區塊鏈平臺&#xff0c;支持智…

nano banana官方最強Prompt模板來了!六大場景模板詳解

最近&#xff0c;相信大家的朋友圈和社交媒體都被一個叫nano banana的AI圖像生成工具刷屏了。從精致的手辦模型到名畫人物穿越&#xff0c;再到中土世界場景還原&#xff0c;nano banana已然成為了最火爆的“整活工具之一。剛剛&#xff0c;谷歌為nano banana推出了官方Prompt模…

LeetCode 2825.循環增長使字符串子序列等于另一個字符串

給你一個下標從 0 開始的字符串 str1 和 str2 。 一次操作中&#xff0c;你選擇 str1 中的若干下標。對于選中的每一個下標 i &#xff0c;你將 str1[i] 循環 遞增&#xff0c;變成下一個字符。也就是說 ‘a’ 變成 ‘b’ &#xff0c;‘b’ 變成 ‘c’ &#xff0c;以此類推&a…

【無人機】1.編譯betaflight和cleanflight的固件

在2023年&#xff0c;betaflight騰飛&#xff0c;而cleanflight已經結束更新&#xff0c;但是用cleanflight的原因是因為他最后版本支持stm32f103系列。不用betaflight因為手頭還沒有f405和f411&#xff0c;只有一個不支持的f407和f401&#xff0c;所以。。接下來開始步驟&…

刻意練習理論

刻意練習理論 一、理論概述 刻意練習&#xff08;Deliberate Practice&#xff09;是由心理學家安德斯艾利克森&#xff08;Anders Ericsson&#xff09;提出的一種系統化學習方法&#xff0c;核心觀點是卓越并非源于天賦&#xff0c;而是通過針對性訓練獲得。其理論基礎來自對…

【FastDDS】Layer DDS之Domain ( 04-DomainParticipantFactory)

Fast DDS 域參與者工廠&#xff08;DomainParticipantFactory&#xff09;詳解 一、域參與者工廠&#xff08;DomainParticipantFactory&#xff09;基礎定義 域參與者工廠&#xff08;DomainParticipantFactory&#xff09;的唯一作用是實現域參與者&#xff08;DomainPartici…

樹莓集團建數字產業學院:產教融合強化成渝人才鏈與產業鏈銜接

成渝地區雙城經濟圈建設是國家重大發展戰略&#xff0c;而人才鏈與產業鏈的有效銜接&#xff0c;是推動成渝地區產業高質量發展的關鍵。樹莓集團順應時代發展需求&#xff0c;搭建數字產業學院&#xff0c;以產教融合為紐帶&#xff0c;不斷強化成渝人才鏈與產業鏈的銜接&#…

在 ASP.NET 8 WebAPI 中使用不同的提供程序驗證多個令牌(Token)及常見問題解答

介紹作為 ASP.NET 框架的最新版本&#xff0c;ASP.NET 8提供了強大的功能&#xff0c;可用于構建安全且可擴展的 Web API。API 開發的一個關鍵方面是身份驗證&#xff0c;它確保只有授權用戶或服務才能訪問受保護的資源。在本文中&#xff0c;我們將探討如何在 ASP.NET 8 API 中…

工業相機為啥丟包?黑條 / 撕裂的原因 + 解決辦法,一看就懂

工業相機為啥丟包&#xff1f;黑條/撕裂的原因解決辦法&#xff0c;一看就懂 工業相機拍圖時出現黑條、撕裂、花屏&#xff0c;別急著換設備——大概率是“數據丟包”在搞鬼。尤其是高頻率、高分辨率采圖時&#xff0c;數據傳輸稍出問題&#xff0c;圖像就會出故障。今天用“快…

【IQA技術專題】NIQE代碼講解

本文是對NIQE圖像質量評價指標的代碼解讀&#xff0c;原文解讀請看NIQE文章講解。 本文的代碼來源于IQA-Pytorch工程。 1、原文概要 NIQE實現了無參考的圖像質量評價指標&#xff0c;可以有效地對圖像的感知&#xff08;Fidelity&#xff09;質量進行評估。本文提出了一種完全…

配置時鐘分頻與倍頻

在STM32微控制器中&#xff0c;“配置時鐘分頻與倍頻”是一個關鍵步驟&#xff0c;它允許開發者根據應用需求調整系統時鐘的頻率。以下是對這一概念的詳細解釋&#xff1a;時鐘源與基礎頻率時鐘源&#xff1a;STM32微控制器通常支持多種時鐘源&#xff0c;如高速外部時鐘&#…

【深度學習新浪潮】視覺大模型在預訓練方面有哪些關鍵進展?

近年來,視覺大模型在預訓練領域取得了多項突破性進展,涵蓋架構設計、多模態融合、數據利用效率及訓練策略等多個維度。以下結合2024-2025年最新研究成果,從技術創新和應用突破兩方面展開分析: 一、架構創新:突破分辨率與模態限制 超高分辨率預訓練 伯克利與英偉達提出的P…

Elasticsearch原理篇

Elasticsearch原理篇寫在前面&#xff1a;用之于手&#xff0c;先明于心一、傳統數據庫的瓶頸&#xff1a;當數據量成為負擔1. 千萬級數據下的性能衰減2. 分頁查詢的“深水陷阱”3. 關聯查詢的擴展難題4. 全文檢索能力薄弱二、Elasticsearch 的優勢&#xff1a;為搜索而生的分布…

《我是如何用C語言寫工控系統的漏洞和Bug》連載(1)內容大綱

第一部分&#xff1a;導論與基礎 第1章 引言 1.1 工控系統的獨特性和重要性 實時性、可靠性、長生命周期的要求與IT系統的差異&#xff1a;后果不再是信息泄露&#xff0c;而是物理世界的中斷與破壞 1.2 為什么C語言依然是工控領域的主流&#xff1f; 性能、底層硬件操作、歷史…

.Net程序員就業現狀以及學習路線圖(三)

一、.Net程序員就業現狀分析 1. 市場需求與薪資水平 ?市場需求兩極分化?&#xff1a;2025年數據顯示&#xff0c;.Net開發崗位全國占比約0.009%&#xff0c;主要集中在深圳、上海等一線城市 2 3。高端崗位&#xff08;云原生/AI集成方向&#xff09;年薪可達36-60萬&#xff…

云計算學習100天-第40天 -普羅米修斯1

目錄 Prometheus 概述—— 安裝prometheus 案例 環境說明 實驗步驟 一、prometheus服務器配置時間同步 二、安裝Prometheus服務器 配置文件說明 三、編寫服務啟動文件并啟動服務 四、訪問web頁面 Prometheus 概述—— Prometheus是一個開源系統監控和警報工具包&a…

高效文本處理:cut、sort、uniq 和 tr 命令詳解與實戰

前言 &#x1f52a; 一、cut —— 按列或字符截取 常用選項&#xff1a; 示例&#xff1a; &#x1f504; 二、sort —— 排序&#xff08;默認按行首字符升序&#xff09; 常用選項&#xff1a; 示例&#xff1a; &#x1f9fc; 三、uniq —— 去除連續重復行 常用選項…

時序數據庫選型指南:Apache IoTDB為何成為工業物聯網首選?

引言&#xff1a;時序數據管理的時代挑戰 隨著工業4.0和物聯網技術的快速發展&#xff0c;全球時序數據呈現爆炸式增長。據IDC預測&#xff0c;到2025年&#xff0c;全球物聯網設備產生的數據量將達到79.4ZB&#xff0c;其中超過60%為時序數據。這類數據具有顯著特征&#xff…