返回

探索 TypeScript 类型系统中的协变和逆变

前端

TypeScript 类型中的逆变协变

类型推导是TypeScript中最强大的特性之一,它允许编译器自动推断变量和表达式的类型。在类型推断过程中,TypeScript 会使用协变和逆变规则来确定类型的兼容性。

协变

协变是指当子类型的值可以安全地赋值给父类型时,子类型和父类型是兼容的。例如,stringany 的子类型,因为任何字符串值都可以安全地赋值给 any 变量。

逆变

逆变是指当父类型的值可以安全地赋值给子类型时,父类型和子类型是兼容的。例如,anystring 的父类型,因为任何值都可以安全地赋值给 string 变量。

双向协变

双向协变是指子类型和父类型都可以安全地相互赋值。例如,numberstring 是双向协变的,因为数字值可以安全地赋值给字符串变量,反之亦然。

不变

不变是指子类型和父类型都不能安全地相互赋值。例如,booleannumber 是不变的,因为布尔值不能安全地赋值给数字变量,反之亦然。

类型推断中的协变和逆变

TypeScript 在类型推断过程中会使用协变和逆变规则来确定类型的兼容性。例如,在下面的代码中:

function foo(x: string) {
  return x.toUpperCase();
}

let y: any = "hello";

foo(y);

TypeScript 会使用协变规则来确定 stringany 是兼容的,因此 foo() 函数可以被调用。

在下面的代码中:

function bar(x: any) {
  x.toUpperCase();
}

let z: string = "hello";

bar(z);

TypeScript 会使用逆变规则来确定 anystring 是兼容的,因此 bar() 函数可以被调用。

协变和逆变的应用

协变和逆变在TypeScript中有很多应用,其中一些最常见的应用包括:

  • 泛型编程:协变和逆变允许泛型类型在不同的类型上工作,而无需重新编写代码。
  • 函数重载:协变和逆变允许函数重载,其中函数的参数或返回值的类型可以根据调用函数的类型而改变。
  • 类型转换:协变和逆变允许在类型之间进行转换,而不会丢失任何信息。

总结

协变和逆变是TypeScript中非常重要的概念,它们允许编译器自动推断变量和表达式的类型。协变和逆变在TypeScript中有很多应用,其中一些最常见的应用包括泛型编程、函数重载和类型转换。

练习题

  1. 以下代码中的 foo() 函数可以被调用吗?为什么?
function foo(x: string) {
  return x.toUpperCase();
}

let y: number = 123;

foo(y);
  1. 以下代码中的 bar() 函数可以被调用吗?为什么?
function bar(x: any) {
  x.toUpperCase();
}

let z: boolean = true;

bar(z);
  1. 以下代码中的 baz() 函数可以被调用吗?为什么?
function baz(x: string | number) {
  return x.toUpperCase();
}

let w: string = "hello";
let v: number = 123;

baz(w);
baz(v);

答案

  1. 不可以。number 不是 string 的子类型,因此 TypeScript 无法使用协变规则来确定它们是兼容的。
  2. 可以。booleanany 的子类型,因此 TypeScript 可以使用逆变规则来确定它们是兼容的。
  3. 可以。stringnumber 都是 string | number 的子类型,因此 TypeScript 可以使用协变规则来确定它们是兼容的。