当前位置: 首页 > news >正文

TypeScript的类型兼容是什么?

TypeScript 的类型兼容性就像一个 “形状检查器”,它关心的是一个值的 “形状”(有哪些属性和方法),而不是它具体是哪个 “品牌”(由哪个类或接口声明)。

核心原则:结构化类型系统 (Structural Typing)

TypeScript 采用的是结构化类型系统(也叫 “duck typing”,鸭子类型:“如果它走路像鸭子,叫起来像鸭子,那它就是鸭子”)。
这与一些其他语言(如 Java、C#)的名义类型系统(Nominal Typing)形成对比。

名义类型系统: 两个类型必须有相同的名称或明确的继承关系才被认为是兼容的。

// Java (名义类型)
class Dog { public void bark() {} }
class Cat { public void bark() {} } // 即使方法一样Dog d = new Cat(); // 编译错误!因为 Dog 和 Cat 是不同的“名义”类型

结构化类型系统 : 两个类型只要结构上满足要求(一个类型的所有成员都能在另一个类型中找到对应且兼容的成员),它们就是兼容的。

// TypeScript (结构化类型)
class Dog { bark() {} }
class Cat { bark() {} } // 结构上与 Dog 兼容let myDog: Dog;
myDog = new Cat(); // ✅ 编译通过!因为 Cat 有 bark 方法,满足 Dog 的结构要求
1. 基本规则:子类型可以赋值给父类型

在 TypeScript 中,如果类型 B 可以赋值给类型 A,我们就说 B 是 A 的子类型 (Subtype)。
判断子类型的基本规则是:B 必须拥有 A 的所有成员,并且类型要兼容。
示例:接口兼容性

interface Point2D {x: number;y: number;
}interface Point3D {x: number;y: number;z: number;
}let p2d: Point2D = { x: 1, y: 2 };
let p3d: Point3D = { x: 1, y: 2, z: 3 };// Point3D 拥有 Point2D 的所有成员 (x, y),所以它是 Point2D 的子类型
p2d = p3d; // ✅ 编译通过// Point2D 缺少 Point3D 的成员 z,所以它不是 Point3D 的子类型
p3d = p2d; // ❌ 编译错误: Property 'z' is missing in type 'Point2D' but required in type 'Point3D'.

结论:成员多的类型可以赋值给成员少的类型。

2. 函数兼容性

函数兼容性是类型兼容中最复杂也最重要的部分。判断一个函数 B 是否能赋值给函数 A,主要看三个方面:
a. 参数数量
目标函数 (A) 的参数数量 必须小于或等于 源函数 (B) 的参数数量。
简单说:你可以 “多给” 参数,但不能 “少要” 参数。

let handler = (a: number) => { console.log(a); };// 参数数量相同,兼容
let handler2 = (a: number, b: string) => { console.log(a, b); };
handler = handler2; // ❌ 编译错误! handler 需要1个参数,但 handler2 需要2个。调用 handler(1) 时会出错。// 反过来
let handler3 = (a: number) => { console.log(a); };
handler2 = handler3; // ✅ 编译通过。调用 handler2(1, "hello") 时,多余的 "hello" 会被忽略,这是安全的。

b. 参数类型
源函数 (B) 的每个参数类型必须能赋值给目标函数 (A) 的对应参数类型。
这与对象兼容性规则一致。

interface Event { type: string }
interface MouseEvent extends Event { x: number; y: number }let onEvent = (e: Event) => { console.log(e.type); };
let onMouseEvent = (e: MouseEvent) => { console.log(e.type, e.x, e.y); };// onMouseEvent 的参数 e 是 MouseEvent 类型,它是 Event 的子类型
// 所以 onMouseEvent 可以赋值给 onEvent
onEvent = onMouseEvent; // ✅ 编译通过。当 onEvent 被调用时,传入的是 Event,而 onMouseEvent 能处理更具体的 MouseEvent,所以处理 Event 也没问题。// 反过来不行
onMouseEvent = onEvent; // ❌ 编译错误! onEvent 的参数 e 是 Event 类型,它可能没有 x, y 属性。

c. 返回值类型
目标函数 (A) 的返回值类型必须能赋值给源函数 (B) 的返回值类型。
这与对象兼容性规则相反。

let createPoint2D = (): Point2D => { return { x: 0, y: 0 }; };
let createPoint3D = (): Point3D => { return { x: 0, y: 0, z: 0 }; };// createPoint3D 的返回值是 Point3D,它是 Point2D 的子类型
// 所以 createPoint3D 可以赋值给 createPoint2D
let factory: () => Point2D;
factory = createPoint3D; // ✅ 编译通过。调用 factory() 期望得到 Point2D,而 createPoint3D 返回了包含更多信息的 Point3D,这是安全的。// 反过来不行
let factory2: () => Point3D;
factory2 = createPoint2D; // ❌ 编译错误! createPoint2D 返回的 Point2D 缺少 z 属性。
3. 特殊情况:函数参数双向协变 (Bivariance)

这是一个 TypeScript 为了兼容性而做的 “非严格” 设计,也是一个常见的坑。
默认情况下,TypeScript 的函数参数是双向协变的。这意味着,不仅子类型可以赋值给父类型,父类型也可以赋值给子类型。

interface Animal { name: string; }
interface Dog extends Animal { breed: string; }
interface Cat extends Animal { purr: boolean; }let checkDog = (dog: Dog) => { console.log(dog.breed); };
let checkAnimal = (animal: Animal) => { console.log(animal.name); };// 子类型赋值给父类型 (正常)
let func1: (a: Animal) => void = checkDog; // ❌ 这在严格模式下会报错!// 父类型赋值给子类型 (双向协变的体现)
let func2: (d: Dog) => void = checkAnimal; // ✅ 编译通过

为什么 func1 有问题?func1 的类型是 (a: Animal) => void,这意味着你可以调用 func1(new Cat())。但 func1 实际上是 checkDog,它会尝试访问 cat.breed,这会导致运行时错误。
如何避免?在 tsconfig.json 中开启 strictFunctionTypes: true,这会使函数参数变为逆变 (Contravariant),从而禁止 func1 这种不安全的赋值。

{"compilerOptions": {"strictFunctionTypes": true}
}
4. 泛型兼容性

泛型的兼容性比较特殊,因为它取决于泛型类型是否被使用。
a. 泛型类型未被使用
如果泛型参数在类型定义中没有被使用,那么它会被当作 any 来处理,因此是兼容的。

interface Empty<T> {}let x: Empty<number>;
let y: Empty<string>;x = y; // ✅ 编译通过,因为 T 没有被使用

b. 泛型类型被使用
如果泛型参数被使用了,那么两个泛型类型只有在它们的类型参数都兼容时才是兼容的。

interface NotEmpty<T> {value: T;
}let x: NotEmpty<number> = { value: 123 };
let y: NotEmpty<string> = { value: "hello" };x = y; // ❌ 编译错误! number 和 string 不兼容

总结
核心: TypeScript 使用结构化类型系统,检查 “形状” 而非 “名义”。
基本规则: 子类型(成员多)可以赋值给父类型(成员少)。
函数兼容:

  • 参数:目标函数参数数量 <= 源函数参数数量。
  • 参数类型:源参数类型是目标参数类型的子类型。
  • 返回值类型:目标返回值类型是源返回值类型的子类型。

注意:函数参数默认是双向协变的,这在某些情况下不安全,建议开启 strictFunctionTypes

泛型: 未使用的泛型参数不影响兼容性,已使用的则要求类型参数也兼容。
理解类型兼容性是掌握 TypeScript 类型系统的关键,它解释了为什么很多看似不同的类型可以相互赋值,以及如何写出更健壮、更安全的代码。

http://www.dtcms.com/a/427982.html

相关文章:

  • 网站怎么提交收录建立网站需要多少钱?
  • PySpark全面解析:大数据处理的Python利器
  • 淮北哪些企业做网站商务网站建设论文总结
  • Kerberos协议深度解析:工作原理与安全实践
  • Linux中断概述
  • 文安做网站的wordpress配置qq邮箱
  • kvmclock
  • 使用 Python 打造一个轻量级系统信息查看器
  • 旅游网站首页制作番禺核酸检测点有新调整
  • 最常用的js加解密之RSA-SHA256 加密算法简介与 jsjiami 的结合使用指南
  • 建站之星至尊版域名中的wordpress删除
  • 苹果软件混淆方式对比与场景化选择,源码混淆、成品包混淆与混合方案
  • 生产环境下oracle19c rac恢复节点2
  • 【VMware】VMware-workstation中,Ubuntu系统安装说明
  • 基于LMK04828的跨板级联时钟同步
  • 黄骅港客运站电话号码企业网站制作策划书
  • 图神经网络分享系列-transe(Translating Embeddings for Modeling Multi-relational Data) (二)
  • 安全的合肥网站建设中国建设银行移动门户
  • LVGL-UI工具
  • 长春网站建设推广网站建设佰金手指科杰二
  • 精益制造——解读麦肯锡集团精益生产与价值流图管理【附全文阅读】
  • 建站吗官方网站农产品网络营销模式
  • 苏宁易购网站设计怎么制作潍坊住房公积金个人查询入口
  • SeaTunnel 同步 KingBase 数据到 Easysearch
  • SSM基于Web的在线音乐网站935wk(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 自己做网站 需要哪些网站绑定别名好吗
  • 【设计模式】六大基本原则
  • dw做的手机端网站雄安网站建设单位
  • SpringBoot 统⼀功能处理
  • 建网站要多少费用南宁个人网站建设