【TypeScript】类型兼容:如何判断一个类型是否可以赋值给其他类型?

本文深入探讨了TypeScript中类型兼容性背后的原理,包括结构化子类型、泛型、变型以及函数类型兼容性。文章通过多个示例解释了如何判断一个类型是否可以赋值给其他类型,强调了类型兼容性在子类型、结构类型、继承和实现中的应用,以及函数参数的逆变性和返回值的协变性。
摘要由CSDN通过智能技术生成

自我介绍:大家好,我是吉帅振的网络日志(其他平台账号名字相同),互联网前端开发工程师,工作5年,去过上海和北京,经历创业公司,加入过阿里本地生活团队,现在郑州北游教育从事编程培训。

一、前言

TypeScript 中类型的兼容性都是基于结构化子类型的一般原则进行判定的。

二、子类型 结构类型 可继承和可实现

(1)子类型

从子类型的角度来看,所有的子类型与它的父类型都兼容,如下代码所示:

{
  const one = 1;
  let num: number = one; // ok
  interface IPar {
    name: string;
  }
  interface IChild extends IPar {
    id: number;
  }
  let Par: IPar;
  let Child: IChild;
  Par = Child; // ok
  class CPar {
    cname = '';
  }
  class CChild extends CPar {
    cid = 1;
  }
  let ParInst: CPar;
  let ChildInst: CChild;
  ParInst = ChildInst; // ok
  let mixedNum: 1 | 2 | 3 = one; // ok
}

在示例中的第 3 行,我们可以把类型是数字字面量类型的 one 赋值给数字类型的 num。在第 12 行,我们可以把子接口类型的变量赋值给 Par。在第 21 行,我们可以把子类实例 ChildInst 赋值给 ParInst。因为成员类型兼容它所属的类型集合(其实联合类型和枚举都算类型集合,这里主要说的是联合类型),所以在示例中的第 22 行,我们可以把 one 赋值给包含字面类型 1 的联合类型。由子类型组成的联合类型也可以兼容它们父类型组成的联合类型,如下代码所示:

  let ICPar: IPar | CPar;
  let ICChild: IChild | CChild;
  ICPar = ICChild; // ok

在示例中的第 3 行,因为 IChild 是 IPar 的子类,CChild 是 CPar 的子类,所以 IChild | CChild 也是 IPar | CPar 的子类,进而 ICChild 可以赋值给 ICPar。

(2)结构类型

类型兼容性的另一准则是结构类型,即如果两个类型的结构一致,则它们是互相兼容的。比如拥有相同类型的属性、方法的接口类型或类,则可以互相赋值。

{
  class C1 {
    name = '1';
  }
  class C2 {
    name = '2';
  }
  interface I1 {
    name: string;
  }
  interface I2 {
    name: string;
  }
  let InstC1: C1;
  let InstC2: C2;
  let O1: I1;
  let O2: I2;
  InstC1 = InstC2; // ok
  O1 = O2; // ok
  InstC1 = O1; // ok
  O2 = InstC2; // ok
}

因为类 C1、类 C2、接口类型 I1、接口类型 I2 的结构完全一致,所以在第 18-19 行我们可以把类 C2 的实例 InstC2 赋值给类 C1 的实例 Inst1,把接口类型 I2 的变量 O2 赋值给接口类型 I1 的变量 O1。在第 20~21 行,我们甚至可以把接口类型 I1 的变量 O1 赋值给类 C1 的实例,类 C2 的实例赋值给接口类型 I2 的变量 O2。另外一个特殊的场景:两个接口类型或者类,如果其中一个类型不仅拥有另外一个类型全部的属性和方法,还包含其他的属性和方法(如同继承自另外一个类型的子类一样),那么前者是可以兼容后者的。

{
  interface I1 {
    name: string;
  }
  interface I2 {
    id: number;
    name: string;
  }
  class C2 {
    id = 1;
    name = '1';
  }
  let O1: I1;
  let O2: I2;
  let InstC2: C2;
  O1 = O2;
  O1 = InstC2;
}

在示例中的第 16~17 行,我们可以把类 C2 的实例 InstC2 和接口类型 I2 的变量 O2 赋值给接口类型 I1 的变量 O1,这是因为类 C2、接口类型 I2 和接口类型 I1 的 name 属性都是 string。不过,因为变量 O2、类 C2 都包含了额外的属性 id,所以我们不能把变量 O1 赋值给实例 InstC2、变量 O2。这里涉及一个需要特别注意的特性:虽然包含多余属性 id 的变量 O2 可以赋值给变量 O1,但是如果我们直接将一个与变量 O2 完全一样结构的对象字面量赋值给变量 O1,则会提示一个 ts(232

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值