TypeScript类型兼容

先看个最简单的例子:接口赋值。TypeScript检查类型兼容性根本不关心具体类型名,只看结构是否匹配。比如你定义了一个Person接口,要求有name和age字段,这时候哪怕你拿一个Dog类型的对象过来,只要它恰好也有同名字段且类型匹配,TypeScript就会放行。这就是著名的"鸭子类型"------走起来像鸭子,叫起来像鸭子,那就是鸭子。

但这里有个细节要注意,源类型必须包含目标类型的所有必需属性。反过来就不行了:

函数兼容性就更有意思了。函数参数检查遵循"逆变"原则------听起来很玄乎,其实很简单:参数类型可以比定义的更宽泛,但不能更严格。比如下面这个例子:

为什么func2赋值给func1不行?因为func2定义时承诺能处理string和number,但func1只能处理number,如果把func2交给func1,碰到string参数就崩了。

返回值则相反,遵循"协变"------返回值类型必须更具体或完全相同:

数组和元组的兼容规则经常让人栽跟头。普通数组看类型参数是否兼容:

但元组就严格多了,必须长度和每个位置的类型都匹配:

枚举类型比较特殊,数字枚举和number互相兼容,但字符串枚举谁也不认:

类的兼容性规则有点反直觉。只比较实例成员,静态成员和构造函数不参与比较。而且private和protected成员会影响兼容性:

泛型兼容性最容易让人困惑。泛型本身不参与兼容性判断,最终看的是具体类型:

any类型在兼容性检查中是万能牌,但也很危险。unknown只能赋值给any和unknown自身,而never可以赋值给任何类型。

实际开发中,掌握这些规则能帮你避免很多奇怪的错误。特别是在使用第三方库时,经常需要自定义类型声明,理解兼容性规则就能写出更健壮的类型定义。记住TypeScript的类型系统是结构化的,这点和很多传统静态语言完全不同。

最后给个实用建议:当遇到复杂的类型兼容问题时,可以先把鼠标悬停在变量上查看推断出的类型,然后用类型断言临时绕过检查。但长期来说,还是应该理顺类型关系,毕竟类型安全的本质是为了减少运行时错误,而不是和编译器斗智斗勇。

相关推荐
攀登的牵牛花2 分钟前
前端向架构突围系列 - 状态数据设计 [8 - 4]:有限状态机 (FSM) 在复杂前端逻辑中的应用
前端
Lsx_2 分钟前
前端视角下认识 AI Agent 和 LangChain
前端·人工智能·agent
陈振wx:zchen200828 分钟前
JavaScript
javascript·js
我是伪码农36 分钟前
Vue 智慧商城项目
前端·javascript·vue.js
不认输的西瓜38 分钟前
fetch-event-source源码解读
前端·javascript
用户390513321928840 分钟前
前端性能杀手竟然不是JS?图片优化才是绝大多数人忽略的"降本增效"方案
前端
朱昆鹏1 小时前
开源 Claude Code + Codex + 面板 的未来vibecoding平台
前端·后端·github
lyrieek1 小时前
pgadmin的导出图实现,还在搞先美容后拍照再恢复?
前端
永远是我的最爱2 小时前
基于.NET的小小便利店前台收银系统
前端·sqlserver·.net·visual studio
从文处安2 小时前
「九九八十一难」第一难:前端数据mock指南(TS + VUE)
前端