9. TypeScript 泛型

TypeScript 中的泛型使开发者能够编写灵活、可重用的代码,同时保持类型安全。它们允许动态定义类型,确保函数、类和接口可以适用于任何数据类型。这有助于避免重复,提高代码的模块化,使其既类型安全又具备适应性。

一、认识泛型

当程序员需要创建可重用组件时,可以使用 TypeScript 的泛型类型,因为它们用于创建可以处理各种数据类型的组件,并提供类型安全性。可重用组件可以是类、函数和接口。TypeScript 泛型可以以多种方式使用,例如函数泛型、类泛型和接口泛型。

(一) 语法

function functionName<T> (returnValue : T) : T {return returnValue;
}

(二) 用法示例

1. 例一

在这个例子中,我们创建了一个可以接受任意类型数据的泛型函数。我们只需要在参数中传入任何类型的数据,然后就可以通过 reverseArray 函数对该值进行反转。

function reverseArray<T>(array: T[]): T[] {return array.reverse();
}const strArray: string[] = reverseArray(["Java", "Python", "C++"]);
const numArray: number[] = reverseArray([1, 2, 3, 4, 5]);
const boolArray: boolean[] = reverseArray([false, true]);console.log(strArray);
console.log(numArray);
console.log(boolArray); 

输出:

[ 'C++', 'Python', 'Java' ]
[ 5, 4, 3, 2, 1 ]
[ true, false ]

2. 例二

在这个例子中,我们创建了一个泛型接口,通过它我们创建了一个对象和字符串类型的值,并将它们打印在控制台中。

// 定义一个泛型接口 Resource,T 是类型参数
interface Resource<T> {id: number;               // 资源 IDresourceName: string;     // 资源名称data: T;                  // 泛型数据字段,类型由使用者决定
}// 使用对象类型实现泛型接口
const person: Resource<object> = {id: 1,                    // 资源 ID 为 1resourceName: 'Person',   // 资源名称为 "Person"data: {                   // data 是一个对象类型name: 'John',         // 姓名age: 25               // 年龄}
}
console.log(person);          // 打印 person 对象// 使用字符串数组类型实现泛型接口
const employee: Resource<string[]> = {id: 2,                      // 资源 ID 为 2resourceName: 'Employee',   // 资源名称为 "Employee"data: ['Employee 1', 'Employee 1']  // data 是一个字符串数组
}
console.log(employee);          // 打印 employee 对象

输出:

{"id": 1,"resourceName": "Person","data": {"name": "John","age": 25}
} 
{"id": 2,"resourceName": "Employee","data": ["Employee 1","Employee 1"]
} 

二、泛型函数

TypeScript 泛型函数允许你创建可以处理多种类型的数据的函数,同时保持类型安全。通过使用尖括号(<T>)中定义的类型参数,泛型使函数能够在不同的数据类型上运行,而不会失去 TypeScript 类型检查所带来的优势。

(一) 语法

function functionName<T>(parameterName: T): ReturnType {// 函数实现
}

这里:

  • functionName 是你的泛型函数的名称。
  • <T> 是类型参数 T,它允许你在函数内部处理不同的数据类型。
  • parameterName 表示函数参数的名称,T 指定该参数接受的数据类型。
  • ReturnType 指定函数预期返回值的类型。

(二) 用法示例

1. 例一

TypeScript 中带有单个类型参数的泛型函数能够处理多种数据类型,同时保证类型安全。你可以显式指定类型,也可以让 TypeScript 推断参数和返回值的类型。

function foo<T>(arg: T): T {return arg;
}let result1: string = foo<string>("TYPESCRIPT");
let result2: number = foo<number>(740);
let result3: boolean = foo<boolean>(false);console.log(result1);
console.log(result2);
console.log(result3);

输出:

GEEKSFORGEEKS
740
false

2. 例二

带有数组参数的泛型函数允许你处理不同类型的数组。通过使用类型参数,该函数可以处理各种元素类型的数组,同时保持类型安全。

function arrayEl<T>(arr: T[]): void {for (const x of arr) {console.log(x);}
}let elements: number[] = [101, 102, 103];
arrayEl(elements);let elements1: string[] = ["吃饭", "睡觉", "打豆豆"];
arrayEl(elements1);

输出:

101
102
103
吃饭
睡觉
打豆豆

3. 例三

在这个例子中,函数 mergeArrays 将两种不同类型的数组合并为一个。它接收类型为 T 和 U 的数组,返回类型为 (T | U)[] 的数组。

function mergeArrays<T, U>(arr1: T[], arr2: U[]): (T | U)[] {return [...arr1, ...arr2];
}// 不同类型的数组
const numbers: number[] = [1, 2, 3];
const words: string[] = ["hello", "world"];// 合并不同类型的数组
const mergedArray: (number | string)[] = mergeArrays(numbers, words);// 输出合并后的数组
console.log(mergedArray);

输出:

[1, 2, 3, "hello", "world"]

(三) 总结

在 TypeScript 中,泛型函数允许你构建能够处理不同数据类型的通用函数,同时确保代码的类型安全,避免错误。这种灵活性非常实用,尤其是在你希望创建能够应对多种情况而无需重复代码的函数时。无论是处理数组、对象还是其他数据结构,泛型函数都能帮助你编写更简洁、更安全的代码。

三、泛型约束

在 TypeScript 中,泛型约束(Generic Constraints)通过使用 extends 关键字来限制可以与泛型类型一起使用的类型。这确保了泛型类型遵循特定的结构或接口,从而能够在泛型中访问某些属性或方法。

(一) 什么是泛型约束?

  • TypeScript 的泛型使我们能够编写可重用的代码,能够处理不同的数据类型。我们可以创建可以与不同数据类型一起工作的函数、类或接口。
  • 泛型使用 <T> 的形式定义,这种 T 类型可以用于函数参数、返回值等的类型定义。
  • 泛型约束用于对泛型类型参数可使用的类型施加限制。
  • 这种限制带来了类型检查,确保变量在被使用之前具备某种类型的值。
  • 这种检查机制有助于最大限度地减少运行时错误的发生。

语法:

function genericConstraintFunction<T extends GenericConstraintType>(param: T): void {// ...
}

含义如下:

  • T 是泛型类型参数。
  • extends GenericConstraintType 指定了一个约束条件,要求类型参数 T 必须继承或符合 GenericConstraintType 类型的结构。

(二) 用法示例

1. 例一

在此示例中,Sports 接口具有一个 name 属性。printSportName 函数使用 extends 来确保其参数在执行前符合 Sports 类型的结构。

// 使用 name 属性定义 Sports 接口
interface Sports {name: string;
}// 定义函数,确保传入的类型 T 继承自 Sports
function printSportName<T extends Sports>(sport: T): void {console.log(sport.name); // 输出运动名称
}// 创建一个 Sports 类型的对象
let sport: Sports = { name: "棒球" };// 使用 sport 对象调用函数
printSportName(sport);

输出:

baseball

2. 例二

在此示例中,我们使用 extends keyof 来确保泛型类型参数 K 是类型 T 的一个键。这种做法强制要求 K 必须是 T 的一个有效属性。

interface Sports {name: string;players: number;
}// 泛型函数,限制 T 必须扩展自 Sports,K 必须是 T 的键
function getNumberOfPlayers<T extends Sports, K extends keyof T>(sport: T, players: K): T[K] {return sport[players];}let sport: Sports = { name: "baseball", players: 9 };// 'players' 被推断为 number 类型
let players: number = getNumberOfPlayers(sport, 'players');
console.log(`球员人数为:${players}`);

输出:

球员人数为:9

3. 例三

在此示例中,我们确保泛型类型参数的类对象实现了特定的接口。

// 定义一个接口 Sports
interface Sports {name: string;players: number;getNumberOfGloves(): number; // 获取手套数量的方法
}// 定义一个实现 Sports 接口的类 Baseball
class Baseball implements Sports {constructor(public name: string, public players: number) { }// 实现接口中的方法getNumberOfGloves(): number {return this.players * 2;}
}// 定义一个泛型函数,约束类型 T 必须实现 Sports 接口
function getNumberOfGloves<T extends Sports>(sport: T): void {console.log(`所需手套数量为:${sport.getNumberOfGloves()}`);
}// 创建 Baseball 类的实例
let baseball = new Baseball("baseball", 9);
getNumberOfGloves(baseball); // 输出:所需手套数量为:18

输出:

所需手套数量为:18

四、泛型接口

在 TypeScript 中创建带有泛型类型的接口是一项强大的功能,它提升了代码的灵活性和可复用性。通过定义带有泛型类型参数的接口,你可以创建适用于多种数据类型的组件,同时保持类型安全。

(一) 带有单一泛型类型参数的接口

interface InterfaceName<T> {// 接口成员
}

定义一个带有单一泛型类型参数的接口,可以创建通用组件,这些组件能够在不同数据类型间灵活操作。此方法提升了代码的灵活性,并通过抽象具体的数据实现,促进代码的重用。

下面的代码创建了一个带有单个泛型类型参数的接口。

interface Box<T> {getItem(): T;setItem(item: T): void;
}class MyBox<T> implements Box<T> {private item: T;getItem(): T {return this.item;}setItem(item: T): void {this.item = item;}
}const stringBox: Box<string> = new MyBox<string>();
stringBox.setItem("Hello, Generics!");
console.log("字符串的值:", stringBox.getItem());

输出:

字符串的值:Hello, Generics!

(二) 多泛型类型参数

TypeScript 也支持带有多个泛型类型参数的接口,使开发者能够设计高度可定制且参数化的组件。

下面的代码创建了一个带有多个泛型类型参数的接口。

interface Pair<K, V> {key: K;value: V;
}const pair: Pair<number, string> = { key: 1, value: "One" };
console.log("Key:", pair.key, ", Value:", pair.value);

输出:

Key: 1,  Value: One 

五、泛型与类

TypeScript 中在泛型中使用类类型,可以让你通过指定泛型参数为类的构造函数,创建更灵活且可重用的代码。当你想操作类的实例,同时又希望保持类型安全时,这种方式特别有用。你可以定义一个接受类构造函数的泛型类型,然后使用该类型来创建和操作这些类的实例。

(一) 语法

function createInstance<T>(constructor: new (...args: any[]) => T,...args: any[]
): T {// 创建并返回指定类的实例return new constructor(...args);
}

说明:

  • T 是一个泛型类型参数,表示要创建的实例的类型。它允许你指定工厂函数的期望返回类型。
  • constructor 是类构造函数的引用。
  • new (...args: any[]) => T 表示该构造函数可以接受任意数量的参数(...args: any[]),并返回类型为 T 的实例。
  • ...args: any[] 使用剩余参数语法,接受任意数量的额外参数,这些参数会在创建实例时传递给构造函数。

(二) 用法示例

1. 例一

在这个例子中,我们定义了一个名为 createInstance 的泛型工厂函数。它接受一个类的构造函数(constructor)和任意数量的附加参数(...args)。createInstance 函数通过使用传入的参数调用构造函数,创建并返回指定类的实例。

// 定义一个泛型工厂函数
function createInstance<T>(constructor:new (...args: any[]) => T, ...args: any[]): T {return new constructor(...args);
}// 示例类
class Product {constructor(public name: string,  // 产品名称public price: number) { }  // 产品价格
}class Person {constructor(public name: string,  // 人名public age: number) { }  // 年龄
}// 使用工厂函数创建实例
const laptop = createInstance(Product, 'Laptop', 999);
console.log(laptop);const user = createInstance(Person, 'TypeScript', 30);
console.log(user);

输出:

Product { name: 'Laptop', price: 999 }
Person { name: 'TypeScript', age: 30 }

2. 例二

在这个例子中,我们定义了一个基类 Animal,包含一个 name 属性和一个 makeSound 方法。Dog 类继承自 Animal,新增了 breed 属性并重写了 makeSound 方法。我们创建了一个泛型函数 printAnimalInfo,该函数接受一个类型为 T 的参数,且 T 必须继承自 Animal 类。这意味着它可以接受 Animal 类或其子类的实例。然后我们创建了 Animal 和 Dog 的实例,并分别调用 printAnimalInfo 函数。

// 定义基类 Animal
class Animal {// 构造函数,初始化公开属性 nameconstructor(public name: string) { }// 返回动物发出的通用声音makeSound(): string {return "Some generic animal sound";}
}// 定义继承自 Animal 的子类 Dog
class Dog extends Animal {// 构造函数,接收 name 和 breed 两个参数constructor(name: string, public breed: string) {super(name);  // 调用父类构造函数,初始化 name}// 重写父类的 makeSound 方法,返回狗叫声makeSound(): string {return "Woof! Woof!";}
}// 定义泛型函数,要求类型 T 继承自 Animal
function printAnimalInfo<T extends Animal>(animal: T): void {// 打印动物的名字console.log(`Name: ${animal.name}`);// 打印动物的叫声(调用 makeSound 方法)console.log(`Sound: ${animal.makeSound()}`);}// 创建 Animal 类的实例
const genericAnimal = new Animal("Generic Animal");
// 创建 Dog 类的实例
const myDog = new Dog("Buddy", "Golden Retriever");// 使用泛型函数,分别传入 Animal 和 Dog 实例
printAnimalInfo(genericAnimal);
printAnimalInfo(myDog);

输出:

Name: Generic Animal
Sound: Some generic animal sound
Name: Buddy
Sound: Woof! Woof!

六、泛型对象类型

TypeScript 泛型对象类型允许你为对象创建灵活且可复用的类型定义。这些泛型类型可以处理不同结构的对象,同时保证类型安全,确保代码既健壮又具有适应性。它们特别适合用于创建能够处理多种对象结构的函数或类,同时保持类型的正确性。

(一) 语法

type MyGenericObject<T> = {key: string;value: T;
};

(二) 用法示例

1. 键值对

在此示例中,我们创建了一个泛型对象类型来表示键值对。类型参数 T 表示值的类型,使得可以使用不同的数据类型。

type KeyValuePair<T> = {key: string;value: T;
};const stringPair: KeyValuePair<string> = { key: 'name', value: 'John' };
const numberPair: KeyValuePair<number> = { key: 'age', value: 30 };console.log(stringPair);
console.log(numberPair);

输出:

{ key: 'name', value: 'John' }
{ key: 'age', value: 30 }

2. 封装数据属性

在此示例中,我们定义了一个泛型对象类型,用于封装指定类型 T 的数据属性。这个例子展示了泛型对象类型如何存储和访问不同的数据类型。

type DataContainer<T> = {data: T;
};const numericData: DataContainer<number> = { data: 25 };
const stringData: DataContainer<string> = { data: 'TypeScript' };console.log(numericData.data);
console.log(stringData.data);

输出:

25
TypeScript

(三) 总结

TypeScript 的泛型对象类型提供了一种强大的方式来创建灵活、可复用且类型安全的对象定义。通过利用这些类型,你可以构建更健壮且适应性更强的代码,确保函数和类能够处理不同的对象结构,同时保持严格的类型正确性。这种方法提升了代码的可维护性,降低了出错的风险,成为 TypeScript 开发者工具箱中不可或缺的重要工具。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/84066.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/84066.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Apache Iceberg与Hive集成:分区表篇

一、Iceberg分区表核心概念与Hive集成原理 1.1 分区表在大数据场景的价值 在大规模数据分析中&#xff0c;分区表通过将数据按特定维度&#xff08;如时间、地域、业务类型&#xff09;划分存储&#xff0c;可显著提升查询效率。Apache Iceberg的分区表设计融合了Hive的分区理…

SAST + IAST + DAST 全链路防护体系构建方案

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 1. 生命周期分层嵌入&#xff08;防御纵深&#xff09; 阶段工具防护重点集成触发点编码阶段SAST源码漏洞&#xff08;硬编码密钥、SQL注入模式&#x…

pnpm link如何不踩坑

前提一&#xff1a;我有一个source-project源码库&#xff0c;有一个develop-project项目库。想使用pnpm link对source-project进行本地调试。 前提二&#xff1a;source-project源码库已打包 本地调试详细步骤如下&#xff1a; 1、检查是否配置了系统环境变量&#xff0c;P…

vue3 javascript 多字段求和技巧

在 Vue 3 中&#xff0c;如果你需要在一个组件中处理多个字段的求和&#xff0c;你可以通过计算属性&#xff08;computed properties&#xff09;或者方法&#xff08;methods&#xff09;来实现。这里我将展示两种主要的方法&#xff1a; 方法 1&#xff1a;使用计算属性&am…

【LeetCode】每日一题 —— No.3405

LeetCode 3405 统计恰好有 K 个相等相邻元素的数组数目&#xff08;DP 构造型&#xff09; 题目概述 我们需要统计长度为 n 的数组 arr 满足如下条件的方案数&#xff1a; 每个元素在区间 [1, m] 之间恰好存在 k 个位置 i (1 ≤ i < n) 满足 arr[i] arr[i - 1] 也就是说…

Elsa Workflows: .NET 的开源工作流引擎简介

文章目录 Elsa Workflows&#xff1a; .NET 的开源工作流引擎核心定位与理念关键特性与优势当前 (Elsa 3) 的已知限制/待完善项总结 Elsa Workflows&#xff1a; .NET 的开源工作流引擎 Elsa Workflows 是一个开源的、模块化的 .NET 库集合&#xff0c;旨在为 .NET 应用程序提…

linux虚拟机yum命令报错解决方案

问题 假如出现了这样的问题&#xff0c;可能是虚拟机yum库存在问题 解决方法 1、打开cmd&#xff0c;输入ssh root地址&#xff0c;比如ssh root192.168.222..111&#xff0c;选yes&#xff0c;输入虚拟机密码 2、使用yum repolist,查看仓库状态&#xff0c;status下面如果是…

C++ 第一阶段 基本语法 - 第一节:变量与数据类型详解

目录 一、变量与数据类型概述 1.1 什么是变量&#xff1f; 1.2 数据类型分类 二、基本数据类型详解 2.1 整型&#xff08;int, short, long&#xff09; 2.1.1 常见整型类型 2.1.2 代码示例 2.1.3 注意事项 2.2 浮点型&#xff08;float, double&#xff09; 2.2.1 浮…

CppCon 2017 学习:CNL: A Compositional Numeric Library

你说的这段关于浮点数的问题总结得很精准&#xff0c;我帮你整理一下&#xff0c;让理解更清晰&#xff1a; The Problem with Floating-Point&#xff08;浮点数的问题&#xff09; 复杂的表示结构 浮点数由符号位 &#xff0c;有效数&#xff08;significand/mantissa&…

linux基础重定向及组合重定向

一、基础重定向操作符 ‌类别‌ ‌操作符‌ ‌含义‌ ‌示例‌ ‌备注‌ ‌标准输出‌ > 覆盖写入 stdout 到文件 ls > file.txt 文件不存在则创建&#xff0c;存在则清空内容 >> 追加 stdout 到文件末尾 date >> log.txt 保留原有内容 ‌标准…

佰力博科技与您探讨铁电分析仪适用场景

铁电分析仪是一种用于测试和研究铁电材料性能的精密仪器&#xff0c;其适用场景非常广泛&#xff0c;涵盖了材料科学、物理学、电子工程等多个领域。 1、材料科学与工程 铁电分析仪广泛应用于铁电材料的研究&#xff0c;包括薄膜、厚膜、块体材料以及电子陶瓷等。它能够测试材料…

JVM 内存模型与垃圾回收机制全解析:架构、算法、调优实践

Java 作为一门面向对象的编程语言&#xff0c;其核心优势之一是 “一次编写&#xff0c;到处运行” 的跨平台特性。这一特性背后&#xff0c;Java 虚拟机&#xff08;JVM&#xff09;扮演着至关重要的角色。JVM 不仅负责解释执行字节码&#xff0c;还通过内存管理和垃圾回收机制…

自然语言处理相关基本概念

基本概念章节总结 一、语言学&#xff08;Linguistics&#xff09; 定义 研究语言的本质、结构和发展规律的科学&#xff0c;涵盖语音、文字、语法等属性。分支包括历时语言学、共时语言学、描述语言学等。 核心内容 分析语言的形态、句法、语义等层面&#xff0c;如词素&…

Vue购物车应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计购物车界面4. 创建Vue实例和数据模型5. 实现购物车功能5.1 从本地存储加载数据5.2 监听数据变化保存到本地存储5.3 实现全选/反选功能5.4 计算选中商品的总价和总数量5.5 实现修改商品数量功能5.6 实现删除商品功能5.7 实现结算功能…

双因子认证如何让Windows系统登录更安全?SLA操作系统双因素认证解决方案深度解析

引言&#xff1a;数字化转型下的身份认证危机 在云计算与远程办公普及的2025年&#xff0c;企业信息系统正面临前所未有的安全挑战。微软Azure Virtual Desktop漏洞事件、Citrix数据泄露等安全事件频发&#xff0c;暴露出传统密码认证体系的致命缺陷。据《2025年云安全威胁报告…

FPGA基础 -- Verilog语言要素之值集合

一、Verilog 值集合&#xff08;Value Set&#xff09; Verilog 是一种面向硬件建模的描述语言&#xff0c;为了更真实地模拟硬件行为&#xff0c;它并不仅仅像 C 语言那样只有 0 和 1 两种值&#xff0c;而是采用了四值逻辑&#xff08;Four-valued logic system&#xff09;…

开源一个芯片自由的脱机下载器

一、什么是脱机下载器 简单来说&#xff0c;脱机下载器就是在不连接电脑、不用专业软件的情况下&#xff0c;也能帮你把程序烧录进芯片的工具。只要插上电源、按个按钮&#xff0c;固件就自动下载进 MCU&#xff0c;非常适合量产、售后、维修等场景。 二、芯片自由的背后&…

Rust 学习笔记:关于模式匹配的练习题

Rust 学习笔记&#xff1a;关于模式匹配的练习题 Rust 学习笔记&#xff1a;关于模式匹配的练习题问题一问题二问题三 Rust 学习笔记&#xff1a;关于模式匹配的练习题 参考视频&#xff1a; https://www.bilibili.com/video/BV1YxojYJESm 问题一 以下代码能否通过编译&…

利用tkinter函数构造MD5加密的可视化操作界面

GitHub文档地址&#xff1a; https://github.com/gao7025/auto_entry_md5.git 引言 利用tkinter构造一个图形界面的创建函数&#xff0c;主要实现了文件选择、MD5加密处理、结果预览和下载等功能。下面是主要涉及的功能模块&#xff1a;主框架、文件选择部分、MD5加密部分、结…

ICEM CFD网格生成 | 基本概念与界面工具

基本概念◆ 名称定义 网格&#xff1a;网格是空间离散的单元&#xff0c;用于如下数值仿真 结构 流体 电磁 其他 单元 0D – 节点单元 质量点 约束&#xff0c;加载位置 1D –线单元 Bars, beams, rods, springs 2D 网格边界 2D – 表面/壳单元 - 四边形 - 三角…