什么是泛型?如何使用?

咱们今天聊聊泛型,这玩意儿算是TypeScript里的“大杀器”,也是很多新手觉得抽象的地方。别怕,我给你打个比方:泛型就像个万能模子。你想想看,工厂里做月饼,同一个模子能压出豆沙、五仁、蛋黄各种馅的月饼。泛型就是这个模子,它让你写一套逻辑,却能处理不同类型的“馅料”。

先看个最经典的痛点。不用泛型的时候,你可能会写这么个函数:

function getFirstItem(arr: any[]): any {
    return arr[0];
}

这么写,类型安全就全丢了。你传个数字数组进去,返回的是any,编译器根本不知道它是个数字。这等于又回到JavaScript的蛮荒时代了。

泛型登场,一切都变得清爽。你看:

function getFirstItem<T>(arr: T[]): T | undefined {
    return arr[0];
}

这个T就是类型参数,你可以把它理解成函数的一个特殊“占位符”。调用的时候,TypeScript会根据你实际传入的数组类型,自动把这个T给“填上”。

const num = getFirstItem([1, 2, 3]); // 类型推断为 number | undefined
const str = getFirstItem(['a', 'b']); // 类型推断为 string | undefined

妙啊!现在num能被自动推断成number类型,strstring类型。编辑器能给你准确的提示,如果你试图把num当成字符串来用,编译器立马就会跳出来反对。这就是类型安全,而且代码复用性极高,一套逻辑通吃所有数组类型。

泛型当然不只能用在函数上。在接口和类里,它更是大放异彩。比如定义一个通用的响应体结构:

interface ApiResponse<T> {
    code: number;
    message: string;
    data: T;
}

这个T代表实际的数据类型。你在用的时候,把它具体化就行:

const userResponse: ApiResponse<{name: string, age: number}> = {
    code: 200,
    message: 'ok',
    data: { name: '张三', age: 30 }
};

const listResponse: ApiResponse<string[]> = {
    code: 200,
    message: 'ok',
    data: ['a', 'b', 'c']
};

看,同一个ApiResponse接口,通过传入不同的类型参数,就能完美适配各种业务数据。这比为每个接口都单独定义一种响应类型要聪明得多。

实际用的时候,你还可以给泛型参数加点约束,告诉它“你这个占位符,也不是什么类型都能当的”。比如,要求类型必须有某个属性:

function getLength<T extends { length: number }>(item: T): number {
    return item.length;
}

这样,getLength函数就能安全地用于任何有length属性的对象,比如数组、字符串,而传个数字进去就会报错。

泛型刚开始可能有点绕,我的建议是多写、多试。别一上来就想设计多么复杂的泛型结构。先从最简单的场景开始,比如封装一个获取数组首尾元素的工具函数。等你发现同样的逻辑因为参数类型不同而不得不复制粘贴时,就是泛型该出场的时候了。它让你的代码在保持灵活的同时,牢牢锁住类型安全,这才是现代类型系统的精髓所在。

© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容