赋值兼容性

TypeScript 中存在两种兼容性,即子类型兼容性和赋值兼容性。子类型兼容性与赋值兼容性有着密切的联系,若类型 S 是类型 T 的子类型,那么类型 S 能够赋值给类型 T。在赋值语句中,变量和表达式之间需要满足赋值兼容性;在函数调用语句中,函数形式参数与实际参数之间也要满足赋值兼容性。示例如下:

type T = { x: number };
type S = { x: number; y: number };

let t: T = { x: 0 };
let s: S = { x: 0, y: 0 };
t = s;

function f(t: T) {}
f(t);
f(s);

此例中,ST 的子类型,那么 S 可以赋值给 T,同时可以使用 S 来调用接收 T 类型参数的函数。

子类型兼容性

在绝大多数情况下,如果类型 S 能够赋值给类型 T,那么也意味着类型 S 是类型 T 的子类型。针对这个规律只有以下几种例外情况。

  • any 类型。在赋值兼容性中,any 类型能够赋值给任何其他类型,但 any 类型不是其他类型的子类型,因为 any 类型是顶端类型。

  • 数值型枚举与 number 类型。number 类型可以赋值给数值型枚举类型,但 number 类型不是数值型枚举的子类型,反而数值型枚举是 number 类型的子类型。示例如下:

    enum E {
        A,
        B,
    }
    
    const s: number = 0;
    const t: E = s;
  • 带有可选属性的对象类型。如果对象类型 T 中有可选属性 M,那么对象类型 S 也可以赋值给对象类型 T,即使 S 中没有属性 M。示例如下:

type T = { x: number; y?: number };
type S = { x: number };

const s: S = { x: 0 };
const t: T = s;

此例中,类型 S 能够赋值给类型 T,但是类型 S 不是类型 T 的子类型,因为类型 T 中的属性 y 不能够在类型 S 中找到对应的属性定义。