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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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;引入了许多新特…