TypeScript 结构类型是什么

你有没有过这样的经历:用 JavaScript 写代码时,明明两个对象的属性和方法看起来一模一样,却因为它们来自不同的地方,总觉得“不对劲”,甚至在调用方法时悄悄报错?比如你写了一个处理“水果”的函数,传了一个看起来像苹果的对象进去,结果函数里某一行就因为格式不对崩了。这种时候,TypeScript 里的“结构类型”就能帮你解决这个小麻烦。

简单说,结构类型就是 TypeScript 判断两个类型是否“匹配”的规则——它只看一个东西“长什么样”,也就是有多少属性、每个属性是什么类型,不管它的“出身”是什么。举个生活化的例子:你可以把类型比作“装东西的盒子”,结构类型就像看盒子里的内容,不管盒子上印的是“苹果盒”还是“橘子盒”,只要盒子里都有“可食用、有果皮、内部有果肉”这几样东西,它们在 TypeScript 里就是同一种“水果类型”。

TypeScript 结构类型是什么

怎么判断两个类型“长得一样”?

结构类型的判断核心,就是“比对属性”。如果两个类型的结构完全匹配,就能互相替代。比如你写了一个接口 Fruit,定义它需要有 name(字符串)和 weight(数字)两个属性:

`typescript

interface Fruit {

name: string;

weight: number;

}

`

这时候你新建一个对象 apple = { name: '红富士', weight: 0.3 },它的结构和 Fruit 完全对得上,TypeScript 就会认为这个 apple 就是一个 Fruit,直接可以用在需要 Fruit 的地方。

再换个场景,如果你要给 Fruit 加一个 color 属性,那一个只有 name 和 weight 的对象就不符合了——就像一个盒子如果多了个“带籽”的要求,少了“有重量”的盒子肯定通不过检查。

结构类型和“名义类型”的区别是什么?

可能你会问,为什么不能像现实里的“名义”那样,非要盒子印了“Fruit”才认?这就要说到 TypeScript 和一些其他语言的区别。比如 Java 是“名义类型”,它认的是“盒子上的标签”,哪怕两个盒子内容完全一样,只要标签不同,就不能通用。但 TypeScript 用结构类型,是为了让代码更灵活,不用给每个小对象都贴固定标签。

还是用例子说清楚:假设你有两个对象,一个是 { name: '钢笔', length: 15 }(文具类),另一个是 { name: '尺子', length: 20 }(文具类),它们的结构都是“有 name 字符串、有 length 数字”,那在 TypeScript 里,你甚至可以写一个通用的工具函数,同时处理这两个东西,不用分别写函数。要是换成名义类型的语言,你就得给每个文具单独定义接口,再写两个重复的函数,麻烦又啰嗦。

结构类型的小注意事项

当然,结构类型不是“完全不管结构”,有两个小细节要注意。第一,多余的属性也会被检查——如果函数要求传 Fruit,你硬塞一个多了 origin(产地)的对象,TypeScript 会报错,这是为了避免你不小心传了不该有的属性。第二,可选属性和只读属性不影响结构匹配,比如 Fruit 里的 weight 是可选的,那一个只有 name 的对象也能算 Fruit,就像“水果盒”允许重量不确定,只要有名字就行。

说到底,TypeScript 的结构类型就是一种“看内容不看标签”的规则,它帮你在保留 JavaScript 灵活性的同时,又能补上类型检查的漏洞,让代码少出bug,写起来也更省心。

上一篇 C / C++ 编程语言的核心特性与实际应用场景科普