
摘要:泛型是 TypeScript 中非常强大的特性,它相当于类型占位符 ,定义代码的时候不用写死具体类型,使用的时候再传入真实类型。既实现代码复用,又保留类型检查,避免滥用
any带来的隐患。本文包含泛型函数、泛型接口、泛型类、泛型约束、keyof,附带完整可运行示例。
一、什么是泛型
泛型是类型参数化的能力:
- 定义阶段:只写占位符(如
<T>),不写死string/number; - 使用阶段:再传入实际的具体类型。
两大核心优势
- 代码复用:同一套逻辑,可以适配多种不同数据类型;
- 类型安全 :不会像
any一样关闭类型校验,编译期依旧做类型检查。
社区常用命名约定
T:Type,通用类型K:Key,键类型V:Value,值类型E:Element,元素类型- 多个泛型参数依次使用
T,U,V
二、泛型函数
基础语法
在函数名后面用 <T> 声明泛型参数,T作为类型占位符,用于参数、返回值。
// identity 函数:输入什么类型,返回什么类型
function identity<T>(arg: T): T {
return arg;
}
两种调用方式
-
显式指定类型,明确告诉TS T是什么类型
let str = identity
("hello"); -
自动类型推断,TS根据入参自动推导T,日常开发最常用
let num = identity(123); // TS自动识别 T 为 number
泛型处理数组
T[]代表元素为T类型的数组。示例:获取数组第一个元素
function getFirstElement<T>(arr: T[]): T {
return arr[0];
}
getFirstElement([10,20,30]); // number
getFirstElement(["a","b","c"]); // string
三、泛型接口
接口同样支持泛型参数,让接口适配多种类型,减少重复接口定义。
// 泛型接口,存储一个 value,value 的类型由 T 决定
interface Container<T> {
value: T;
}
// 使用泛型接口
const numBox: Container<number> = { value: 100 };
const strBox: Container<string> = { value: "typescript" };
也可以用来约束函数形状:
interface IdentityFn<T> {
(arg: T): T;
}
const myId: IdentityFn<number> = function(x){ return x; }
四、泛型类
类名后添加<T>,类的属性、方法都可以使用这个泛型类型。
⚠️注意:类中的static静态成员,不能使用泛型参数T。
class GenericNumber<T> {
zeroValue: T;
add: (x:T,y:T) => T;
}
// 数字版本
let numCalc = new GenericNumber<number>();
numCalc.zeroValue = 0;
numCalc.add = (a,b)=> a + b;
// 字符串版本
let strCalc = new GenericNumber<string>();
strCalc.zeroValue = "";
strCalc.add = (a,b)=> a + b;
五、泛型约束 extends
直接使用泛型<T>的时候,TS不知道T具备哪些属性,不能随便访问对象属性。
通过 T extends 类型/接口,给泛型增加约束,限定T必须具备某些成员。
// 定义约束接口,要求必须有 length 属性
interface Lengthwise {
length: number;
}
// T 必须实现 Lengthwise,必须拥有 length
function logLength<T extends Lengthwise>(arg: T): void {
console.log(arg.length);
}
logLength("hello"); // ✅字符串有length
logLength([1,2,3]); // ✅数组有length
// logLength(100); // ❌number没有length,编译报错
keyof:对象属性约束
K extends keyof T,限制K必须是对象T身上合法的属性名,防止访问不存在的key。
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const user = { name: "Alice", age: 30 };
const userName = getProperty(user, "name"); // ✅合法属性
// getProperty(user, "address"); // ❌编译报错,address不存在
六、实战示例:泛型封装本地存储工具
封装一个类型安全的LocalStorage存储类,存入和取出都保持类型约束。
class LocalStorageUtil<T> {
private key: string;
constructor(storageKey: string){
this.key = storageKey;
}
save(data:T):void{
localStorage.setItem(this.key, JSON.stringify(data));
}
retrieve(): T | null {
const str = localStorage.getItem(this.key);
if(!str) return null;
return JSON.parse(str) as T;
}
}
// 使用:存储用户信息
const userStore = new LocalStorageUtil<{name:string,age:number}>("userInfo");
userStore.save({name:"张三",age:20});
const userData = userStore.retrieve();
📝完整可复制Demo代码
保存为generics-demo.ts
// ==================== 泛型函数 ====================
function identity<T>(arg: T): T {
return arg;
}
const s1 = identity<string>("hello");
const n1 = identity(666);
function getFirstElement<T>(arr: T[]): T {
return arr[0];
}
console.log(getFirstElement([1,2,3]));
console.log(getFirstElement(["TS","JS"]));
// ==================== 泛型接口 ====================
interface Container<T> {
value: T;
}
const numBox: Container<number> = { value: 99 };
const strBox: Container<string> = { value: "泛型学习" };
console.log(numBox, strBox);
// ==================== 泛型类 ====================
class GenericNumber<T> {
zeroValue: T;
add: (x:T,y:T) => T;
}
const numCalc = new GenericNumber<number>();
numCalc.zeroValue = 0;
numCalc.add = (a,b)=>a+b;
console.log(numCalc.add(10,20));
// ==================== extends 泛型约束 ====================
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg:T){
console.log(arg.length);
}
logLength("typescript");
logLength([10,20]);
// ==================== keyof 属性约束 ====================
function getProperty<T,K extends keyof T>(obj:T,key:K){
return obj[key];
}
const user = {name:"Alice",age:30};
console.log(getProperty(user,"name"));
// ==================== 实战:LocalStorage工具 ====================
class LocalStorageUtil<T> {
private key: string;
constructor(storageKey:string){
this.key = storageKey;
}
save(data:T){
localStorage.setItem(this.key, JSON.stringify(data));
}
retrieve(): T|null {
const res = localStorage.getItem(this.key);
return res ? JSON.parse(res) : null;
}
}
const store = new LocalStorageUtil<{name:string}>("demo");
store.save({name:"小明"});
console.log(store.retrieve());
小结
- 泛型
<T>是类型占位符,定义不写死类型,使用时指定,兼顾复用和类型安全,替代不安全的any; - 支持泛型函数、泛型接口、泛型类;泛型类
static静态成员不能使用泛型参数; extends实现泛型约束,限制传入类型必须具备某些属性;K extends keyof T约束属性名,在编译阶段拦截非法对象属性访问;- 实际项目中,封装工具类、网络请求、数据存储,泛型都有大量使用。
需要了解课程详情、获取备考资料的小伙伴可以直接点击下方名片咨询!