返回

妙语连珠论协变逆变,带你一探 TypeScript 的灵动之美

前端

协变与逆变:一个妙趣横生的故事

在编程的世界里,协变与逆变犹如两位形影不离的舞者,时而携手共进,时而反向而行,谱写出一曲灵动曼妙的乐章。

协变:灵动扩展,打破界限

协变,犹如一位不断扩张的圆环,每当子类型在内涵上扩展,外延也会随之增多。让我们以TypeScript中的泛型函数为例。假设我们有一个函数 max,它接受两个数字参数并返回较大的一个。如果我们将其泛型化,允许它接受任何类型的数字,那么子类型就可以返回比父类型更多的派生类型。

function max<T extends number>(a: T, b: T): T {
  return a > b ? a : b;
}

// 父类型:数字
max(10, 20); // 返回值:20

// 子类型:浮点数
max(10.5, 20.1); // 返回值:20.1

// 子类型:大整数
max(10n, 20n); // 返回值:20n

正如你所见,子类型 max 可以返回比父类型更多的派生类型,包括浮点数和大整数。这正是协变的魅力所在,它允许子类型在保持内涵一致的情况下,扩展外延,从而提高代码的灵活性。

逆变:反向流动,兼容多态

逆变,恰似一条逆流而上的鱼儿,当子类型在内涵上收缩,外延也会随之减少。让我们继续以TypeScript中的泛型函数为例。假设我们有一个函数 min,它接受两个数字参数并返回较小的一个。如果我们将其泛型化,允许它接受任何类型的数字,那么子类型就可以接受比父类型更少的派生类型。

function min<T extends number>(a: T, b: T): T {
  return a < b ? a : b;
}

// 父类型:数字
min(10, 20); // 返回值:10

// 子类型:整数
min(10, 20.1); // 返回值:10

// 子类型:短整数
min(10, 20n); // 返回值:10

正如你所见,子类型 min 可以接受比父类型更少的派生类型,包括整数和短整数。这正是逆变的奥秘,它允许子类型在保持内涵一致的情况下,收缩外延,从而提高代码的可兼容性。

协变与逆变的奇妙融合,成就TypeScript的灵动之美

协变与逆变,犹如一对珠联璧合的舞者,在TypeScript中翩翩起舞,交织出多姿多彩的乐章。它们使开发人员能够创建更加灵活和可扩展的代码,从而提高应用程序的质量和可维护性。

协变与逆变的应用场景

协变和逆变在TypeScript中有着广泛的应用场景,以下是一些常见的例子:

  • 泛型函数和方法: 协变和逆变可以用于泛型函数和方法中,允许子类型返回比其父类型更多的派生类型,或接受比其父类型更少的派生类型。
  • 类和接口: 协变和逆变可以用于类和接口中,允许子类型继承父类型的协变或逆变属性和方法。
  • 回调函数: 协变和逆变可以用于回调函数中,允许子类型返回比其父类型更多的派生类型,或接受比其父类型更少的派生类型。
  • 事件监听器: 协变和逆变可以用于事件监听器中,允许子类型返回比其父类型更多的派生类型,或接受比其父类型更少的派生类型。

协变与逆变的注意事项

在使用协变和逆变时,需要注意以下几点:

  • 协变和逆变只能用于类型参数,不能用于普通变量。
  • 协变和逆变不能用于类型别名。
  • 协变和逆变不能用于联合类型。
  • 协变和逆变不能用于交叉类型。
  • 协变和逆变不能用于泛型约束。

总结

协变和逆变是TypeScript中两大重要的概念,它们使开发人员能够创建更加灵活和可扩展的代码。协变允许子类型返回比其父类型更多的派生类型,而逆变允许子类型接受比其父类型更少的派生类型。在本文中,我们详细探讨了协变和逆变的内涵和外延,并通过实际例子演示了如何利用它们来提高代码的质量和可维护性。希望通过本文的讲解,你能对协变和逆变有一个更加深入的了解,并将其应用到你的TypeScript开发实践中。