TypeScript 泛型(Generics):类型占位符实现复用与安全

摘要:泛型是 TypeScript 中非常强大的特性,它相当于类型占位符 ,定义代码的时候不用写死具体类型,使用的时候再传入真实类型。既实现代码复用,又保留类型检查,避免滥用any带来的隐患。本文包含泛型函数、泛型接口、泛型类、泛型约束、keyof,附带完整可运行示例。

一、什么是泛型

泛型是类型参数化的能力:

  • 定义阶段:只写占位符(如<T>),不写死 string / number;
  • 使用阶段:再传入实际的具体类型。

两大核心优势

  1. 代码复用:同一套逻辑,可以适配多种不同数据类型;
  2. 类型安全 :不会像any一样关闭类型校验,编译期依旧做类型检查。

社区常用命名约定

  • T:Type,通用类型
  • K:Key,键类型
  • V:Value,值类型
  • E:Element,元素类型
  • 多个泛型参数依次使用 T,U,V

二、泛型函数

基础语法

在函数名后面用 <T> 声明泛型参数,T作为类型占位符,用于参数、返回值。

复制代码
// identity 函数:输入什么类型,返回什么类型
function identity<T>(arg: T): T {
    return arg;
}

两种调用方式

  1. 显式指定类型,明确告诉TS T是什么类型

    let str = identity("hello");

  2. 自动类型推断,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());

小结

  1. 泛型<T>是类型占位符,定义不写死类型,使用时指定,兼顾复用和类型安全,替代不安全的any;
  2. 支持泛型函数、泛型接口、泛型类;泛型类static静态成员不能使用泛型参数;
  3. extends实现泛型约束,限制传入类型必须具备某些属性;
  4. K extends keyof T约束属性名,在编译阶段拦截非法对象属性访问;
  5. 实际项目中,封装工具类、网络请求、数据存储,泛型都有大量使用。

需要了解课程详情、获取备考资料的小伙伴可以直接点击下方名片咨询!

相关推荐
赛博守夜人8 小时前
AI安全攻防与治理之十四:AI 安全测试不能脱离传统安全,Web、API、移动端与模型攻击面的组合拳
前端·人工智能·安全
jimmyleeee9 小时前
Agent安全之八:OWASP ASI08 级联失效------当每一个 Agent 都“正确”,系统却走向灾难
网络·安全·web安全
山东科恩光电10 小时前
安全地毯压敏传感快速响应与耐腐蚀橡胶材质在工业危险区域的防护部署
安全
安教安全员通关宝15 小时前
2026年河北安全员A证考试题库+答案+解析
安全
updayday85415 小时前
客户附件不该发往旧邮箱,如何在发送前拦截 - Ping64 邮件安全管控
大数据·网络·数据库·安全·智能路由器
其实防守也摸鱼15 小时前
Chrome 扩展安全攻防实录:从攻击面到防御实践
开发语言·前端·网络·chrome·安全·web安全·php
领麦微红外15 小时前
红外测温传感器 赋能充电桩热安全与运维智能化
运维·安全·产品经理·智能硬件
2601_9623649717 小时前
企业 PC 安全升级,Windows Hello 商用生物识别模组介绍
windows·安全
猫头_17 小时前
MCP 开发实战:把你自己的后端 API 接入 Agent
人工智能·typescript·mcp