当前位置: 首页 > news >正文

TypeScript 泛型详解:从基础到实战应用

一、开篇三问

1. 你是否这么写过函数?

面对不确定传入和返回值的类型的时候,你是否将函数的参数和返回值类型写成any,这样子ts就失去了类型检查的意义,只能知道数据类型是any,而不知道具体的类型。

2. 想让它 支持 number、string、boolean, 不失去类型推断?

3. 这就是泛型(Generic)存在的意义: 一套代码 → 多种类型 → 编译期检查 → 运行期正确

4. 一句话理解泛型概念:

        泛型 = 把 类型 当成 参数 传进去。
就像函数参数 value: T,只是 T 不是值,而是“某个类型”。

二、泛型基础:语法与核心概念

2.1 泛型函数

泛型通过类型变量表示未指定的类型,在函数调用或类实例化时被具体类型替换。类型变量通常用<T>表示(T即 "Type" 的缩写)。

// T 代表“任意类型”,在调用时由使用者决定
function identity<T>(arg: T): T {return arg;
}// 调用方式 1:显式指定类型
const str = identity<string>('Hello');// 调用方式 2:让 TS 自动推断
const num = identity(42); // 推断为 number

2.2 泛型函数与多类型参数

泛型函数支持多个类型变量,用于处理多种类型的输入输出场景。

// 多类型参数:将两个不同类型的值组合为元组
function pair<T, U>(first: T, second: U): [T, U] {return [first, second];
}// 调用时自动推断类型为[string, number]
const result = pair("age", 25);
console.log(result); // ["age", 25]

 泛型也可用于类的方法(函数)中:

class Container<T> {private value: T;constructor(initialValue: T) {this.value = initialValue;}// 泛型方法:返回T类型getValue(): T {return this.value;}// 泛型方法:接收T类型参数setValue(newValue: T): void {this.value = newValue;}
}// 使用时指定类型为string
const stringContainer = new Container<string>("init");
stringContainer.setValue("update");
// stringContainer.setValue(12)   // 报错指定的参数类型为string,但传入的参数类型为number

 2.3 泛型接口

泛型接口允许接口成员的类型由调用者指定,适用于定义通用数据结构或函数类型。

// 定义泛型接口:描述键值对结构
interface KeyValuePair<K, V> {key: K;value: V;
}// 使用时指定K为string,V为number
const agePair: KeyValuePair<string, number> = {key: "age",value: 12
};// 泛型接口描述函数类型
interface Transformer1<T, U> {(input: T): U;
}// 实现接口:将number转为string
const numberToString: Transformer1<number, string> = (num) => {return num.toString();
};

2.4  泛型类

泛型类通过在类名后添加<T>定义,类内部可使用该类型参数。

// 泛型类:实现栈数据结构
class Stack<T> {// 定义栈private items:T[] =[]// 入栈:接收T类型元素push(item:T){this.items.push(item)}// 出栈:返回T类型元素pop():T | undefined{return this.items.pop()}
}
// 实例化number类型的栈
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 2(类型为number)// 实例化string类型的栈
const stringStack = new Stack<string>();
stringStack.push("a");
console.log(stringStack.pop()); // "a"(类型为string)

2.5 泛型参数默认值

为泛型参数设置默认类型,当未显式指定类型时自动使用默认值:

// 为T设置默认类型string
class Box<T> {value: T;constructor(value: T) {this.value = value;}show(){return this.value;}
}// 未指定类型,默认使用string
const strBox = new Box("hello");
console.log(strBox.show());
// 显式指定为number
const numBox = new Box<number>(123);
console.log(numBox.show());

 2.6 注意:静态成员与泛型的限制

泛型类的静态成员不能使用类的类型参数,因为静态成员属于类本身(加载时确定),而类型参数在实例化时才确定:

三、泛型约束

泛型的灵活性可能导致类型操作不安全(例如访问不存在的属性)。通过泛型约束,我们可以限制类型参数的范围,确保操作合法。

3.1 用extends约束类型

通过extends关键字,要求泛型必须满足某个接口或类型:

// 定义约束:必须包含length属性
interface Haslengrh{length:number;
}
// 约束T必须符合HasLength
function getlength<T extends Haslengrh>(arg:T):T{console.log(arg.length)return arg;
}
// 合法:字符串有length属性
getlength("hello")
// 合法:字符串有length属性
getlength([1,2,3])
// 合法:字符串有length属性
getlength({length:10})
// 错误:数字没有length属性
// getlength(123)

3.2 用keyof约束对象属性

结合keyof操作符(获取对象所有属性名),可确保访问的属性存在于对象中:

// T为对象类型,K为T的属性名
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {return obj[key]; // 安全访问obj的key属性
}const user = { name: "Alice", age: 25 };
// 合法:"name"是user的属性
getProperty(user, "name"); // "Alice"
// 错误:"gender"不是user的属性
getProperty(user, "gender"); 

 四、最佳实践与常见误区

✅ 推荐❌ 不推荐
命名使用 T / K / V 等约定使用无意义的长名称
使用 extends 约束输入滥用 any 放弃类型检查
组合 Utility Types 生成新类型重复声明相似接口
静态方法独立定义泛型在静态成员中引用类级泛型

http://www.dtcms.com/a/287817.html

相关文章:

  • 3.条件判断:让程序学会做选择
  • Web开发 03
  • import.meta.glob 与 import.meta.env、import的几个概念的简单回顾
  • react+antd+表格拖拽排序以及上移、下移、移到顶部、移到底部
  • 408数据结构强化(自用)
  • 实现el-select下拉框,下拉时加载数据
  • MYSQL 第一次作业
  • 《命令行参数与环境变量:从使用到原理的全方位解析》
  • Flink实时流量统计:基于窗口函数与Redis Sink的每小时PV监控系统(学习记录)
  • UniApp 自定义导航栏:解决安全区域适配问题的完整实践
  • C++基于muduo库从零实现Rpc框架
  • Ubuntu18.04环境下,vscode使用clangd、bear实时准确跳转过程中遇到的compile_commands.json无法解析问题
  • windows wsl ubuntu 如何安装 maven
  • 程序混淆的可行性?
  • PyCharm 入门指南:起步学习、开发环境一体
  • java: DDD using sql server 2019 or Oracle21c
  • WLAN Autoconfig 自启动失效/WIFI功能消失问题解决方案
  • Gradle安装教程
  • 深入理解设计模式之模板模式:优雅地定义算法骨架
  • 在RK3588开发板快速搭建ros环境以及运行ros程序(以usb_cam为例)
  • 云服务器搭建自己的FRP服务。为什么客户端的项目需要用Docker启动,服务端才能够访问到?
  • 详细解读Go中的 fmt包
  • 2025年医疗人工智能发展现状
  • JMeter 元件使用详解
  • 初学者STM32—DMA数据转运
  • [DBC教程 一] DBC文件概述及搭建编辑环境CANdb++ Admin
  • [LGR-233-Div.4]洛谷入门赛#37——8道题题解
  • CF 训练 2 D - E参考代码
  • 面试高频题 力扣 130. 被围绕的区域 洪水灌溉(FloodFill) 深度优先遍历(dfs) 暴力搜索 C++解题思路 每日一题
  • Maven 依赖管理