TypeScript 如何做类型合并
你有没有过这种经历:写代码时明明定义了一个对象,结果调用某个属性时却报错,说“不存在这个属性”?比如你做外卖小程序,定义了“用户信息”对象,里面有“姓名”“手机号”,结果不小心调用了“收货地址”属性,直接跳出红标警告。要是没工具提醒,等上线后用户填地址时才出问题,可就麻烦了。这时候TypeScript的类型合并功能,就能帮你解决这个“一个不够用,多个又重复”的问题。
什么是类型合并?
简单说,类型合并就是把两个或多个“类型模板”拼在一起,生成一个包含所有属性的新模板。就像你有两个便利贴,一个写着“姓名、手机号”,另一个写着“收货地址、邮编”,把它们粘在一起,就得到了一张同时有这四个信息的新便利贴,以后用这个新模板,就不用重复写属性了。

最常用的方式:接口继承
举个生活化的例子,你是奶茶店的员工,现在要整理“点单信息”。一开始的基础点单信息是“杯型、口味、数量”,但会员点单时需要额外加“会员编号、积分抵扣”,普通点单不需要这些。这时候用接口继承就很方便:
`typescript
// 基础点单类型
interface BasicOrder {
cupSize: string;
flavor: string;
count: number;
}
// 会员点单类型,继承基础点单,再加会员专属属性
interface MemberOrder extends BasicOrder {
memberId: string;
usePoints: boolean;
}
`
这样写的话,MemberOrder就自动有了cupSize这些基础属性,不用再重复写一遍。就像你先做了一个通用的点单模板,再给会员加了专属标签,既省事又不会漏属性。如果是普通点单,直接用BasicOrder就行,不会有多余的属性。
另一种方式:交叉类型(适合非接口场景)
要是你用的不是接口,而是类型别名,就可以用交叉类型,也就是用&符号把多个类型拼起来。比如你做的是图书管理系统,有“图书基本信息”和“借阅信息”,这两个都是类型别名,合并成“可借阅图书信息”就可以这么写:
`typescript
// 图书基本信息
type BookBase = {
bookId: string;
title: string;
author: string;
};
// 借阅信息
type BookBorrow = {
borrowDate: string;
dueDate: string;
};
// 合并两个类型,得到完整的可借阅图书信息
type BorrowableBook = BookBase & BookBorrow;
`
这样BorrowableBook就同时有了图书的基本信息和借阅日期,就像把图书的“身份证”和“借阅登记本”钉在一起,拿到的就是完整的图书资料。这种方式适合用类型别名的场景,比继承更灵活,比如给数字加额外属性也可以用。
要注意的小坑
虽然类型合并很方便,但也有需要避开的小问题。比如如果两个类型里有同名但类型不一样的属性,合并后这个属性的类型会变成never,也就是“不可能有值”,用的时候会报错。比如你定义了两个类型,一个User里age是number,另一个Admin里age是string,合并后age就成了never,你没法给它传数字也没法传字符串,这就是冲突了,写的时候要尽量避免同名属性的类型不一致。
还有一种特殊情况,就是声明合并——如果同一个接口在不同地方写了两次,TypeScript会自动合并它们,不用手动写&或者extends。比如你在文件A里写了interface User { name: string },文件B里又写了interface User { age: number },最后会自动变成User有name和age两个属性。这就像你在两个地方补了同一个便利贴,最后自动粘成一张,很适合多人协作写代码时,不同模块定义同一个类型的补充属性。
学会类型合并,就能让你的TypeScript代码更清晰,避免重复写属性,还能提前发现类型冲突的问题,减少运行时的错误。就像整理东西时用分层的收纳盒,基础的东西放底层,需要加的东西放上层,不用每次都重新整理,效率高还不容易乱。