当前位置:首页 > 文章列表 > 文章 > 前端 > TypeScript 协变与逆变

TypeScript 协变与逆变

2026-08-21 03:35:30 0浏览 收藏

协变与逆变是 TypeScript 类型系统中的重要概念,理解它们有助于编写类型安全的代码。

TypeScript 协变与逆变

它们描述了泛型类型在父子类型关系中的行为。

协变与逆变概念协变 (Covariant)输出类型:安全Dog → AnimalProvider → Provider不变 (Invariant)输入/输出类型:严格不能相互赋值Consumer ≠ Consumer逆变 (Contra variant)输入类型:反转Animal → DogConsumer → ConsumerTypeScript 默认行为返回值:协变参数:逆变 (strictFunctionTypes)属性:协变

为什么需要协变与逆变

当我们使用泛型类或函数时,类型参数的行为并不像你想象的那么简单。

将 Dog 赋值给 Animal 是安全的,但将处理 Animal 的函数赋值给处理 Dog 的函数可能不安全。

协变与逆变规则帮助 TypeScript 捕获这些潜在的类型错误。

概念:协变允许子类型向父类型转换,逆变允许父类型向子类型转换,不变则不允许任何方向转换。

协变 (Covariant)

协变是指子类型可以赋值给父类型。对于输出类型(如函数返回值),这是安全的。

实例

// 定义动物和狗的类型
class Animal {
    name: string = "动物";
}
class Dog extends Animal {
    breed: string = "田园犬";
}
// 协变:输出类型可以向更宽泛的类型转换
// 返回 Dog 的函数可以赋值给返回 Animal 的函数
type AnimalGetter = () => Animal;
type DogGetter = () => Dog;
// Dog 是 Animal 的子类,所以 DogGetter 可以赋值给 AnimalGetter
const getDog: DogGetter = () => new Dog();
const getAnimal: AnimalGetter = getDog;  // 协变:安全
// 运行
const animal: Animal = getAnimal();
console.log("动物名称: " + animal.name);

运行结果:

动物名称: 动物

返回值协变:函数返回更具体的子类型是安全的,因为返回的对象必然符合父类型的要求。

逆变 (Contra variant)

逆变是指对于输入类型(如函数参数),父类型可以赋值给子类型。

实例

// 定义类型
class Animal {
    name: string = "动物";
}
class Dog extends Animal {
    breed: string = "田园犬";
}
// 逆变:输入类型可以向更具体的类型转换
// 接受 Animal 的函数可以赋值给接受 Dog 的函数
type DogConsumer = (dog: Dog) => void;
type AnimalConsumer = (animal: Animal) => void;
// 如果接受更宽泛类型的函数可以赋值给更具体类型的函数
// 那么当我们传入 Dog 时,函数可能会处理不了(缺少 Dog 特有的属性)
const consumeAnimal: AnimalConsumer = (animal) => {
    console.log("处理动物: " + animal.name);
};
const consumeDog: DogConsumer = consumeAnimal;  // 逆变:安全
// 运行
const dog = new Dog();
dog.breed = "哈士奇";
consumeDog(dog);

参数逆变:函数参数使用逆变,因为接受更具体类型的函数无法处理更宽泛的类型。

启用严格函数类型

在 TypeScript 里,函数参数默认会按逆变规则来检查;而一旦启用 strictFunctionTypes,这套检查机制就会被严格执行起来。

实例

// 定义类型
interface Animal {
    readonly name: string;
}
interface Dog extends Animal {
    readonly breed: string;
}
// 定义函数类型
type GetName = (animal: Animal) => string;
type GetDogBreed = (dog: Dog) => string;
// 正确的赋值
const getDogBreed: GetDogBreed = (dog) => dog.breed;
// 尝试赋值 - 在 strictFunctionTypes 下会报错
// 因为 AnimalConsumer (参数更宽泛) 不能赋值给 DogConsumer (参数更具体)
// 这是因为参数是逆变的
function printAnimalName(animal: Animal): string {
    return animal.name;
}
// 尝试将接受更宽泛类型的函数赋值给更具体类型
// const getSpecific: GetDogBreed = printAnimalName; // 错误!
console.log("犬种: " + getDogBreed({ name: "旺财", breed: "哈士奇" }));

strictFunctionTypes:在 tsconfig.json 里开启这个选项后,函数相关的类型检查会更严格,类型约束也会更严密。

泛型类的协变

泛型类的属性默认是协变的。

实例

// 定义类型
class Animal {
    name: string = "动物";
}
class Dog extends Animal {
    breed: string = "狗";
}
// 泛型容器类
class Cage {
    animal: T;
    constructor(animal: T) {
        this.animal = animal;
    }
}
// 协变:可以子类型容器赋值给父类型容器
const dogCage = new Cage(new Dog());
const animalCage: Cage = dogCage;  // 协变:安全
// animalCage 现在可以安全地当作包含动物的笼子使用
console.log("动物名称: " + animalCage.animal.name);

属性协变:对象的属性是协变的,子类型属性可以赋值给父类型属性。

数组的协变

TypeScript 中数组是协变的,但需要注意可变性带来的问题。

实例

// 定义类型
class Animal {
    name: string = "动物";
}
class Dog extends Animal {
    breed: string = "狗";
}
// 数组协变
const dogs: Dog[] = [
    { name: "旺财", breed: "哈士奇" },
    { name: "小白", breed: "萨摩耶" }
];
// Dog[] 可以赋值给 Animal[]
const animals: Animal[] = dogs;  // 协变:安全
// 问题:虽然类型上安全,但实际上可以添加其他动物
// animals.push({ name: "猫咪", breed: "猫" }); // 运行时可能出问题!
console.log("动物数量: " + animals.length);

数组可变性:协变赋值后修改数组可能导致运行时错误,需要注意。

使用 extends 实现安全赋值

了解协变与逆变后,可以安全地设计泛型接口。

实例

// 定义类型
interface Producer {
    // 生产方法:返回值是协变的
    produce(): T;
}
interface Consumer {
    // 消费方法:参数是逆变的
    consume(value: T): void;
}
// 具体实现
class DogProducer implements Producer {
    produce(): Dog {
        return { name: "旺财", breed: "哈士奇" };
    }
}
class AnimalConsumer implements Consumer {
    consume(animal: Animal): void {
        console.log("消费动物: " + animal.name);
    }
}
// Producer 可以赋值给 Producer(协变)
const animalProducer: Producer = new DogProducer();
// Consumer 可以赋值给 Consumer(逆变)
const dogConsumer: Consumer = new AnimalConsumer();
// 测试
const animal = animalProducer.produce();
console.log("生产: " + animal.name);
dogConsumer.consume({ name: "旺财", breed: "哈士奇" });

设计原则:根据方法的用途选择合适的类型方向,提高 API 的类型安全性。

注意事项

  • 返回值协变:函数返回子类型是安全的
  • 参数逆变:函数参数使用父类型是安全的
  • 启用严格模式:使用 strictFunctionTypes 获得更严格检查
  • 数组协变:注意可变性带来的潜在问题

最佳实践:理解协变与逆变可以帮助设计更类型安全的 API,避免运行时错误。

总结

协变与逆变是 TypeScript 类型系统的核心概念。

  • 协变:子类型 → 父类型,用于输出类型
  • 逆变:父类型 → 子类型,用于输入类型
  • 不变:不能相互赋值
  • strictFunctionTypes:启用严格函数类型检查

建议:在设计泛型 API 时考虑协变与逆变,编写更安全的类型代码。

Python sqlite3.Blob 怎么改大字段:blobopen、固定长度与事务边界Python sqlite3.Blob 怎么改大字段:blobopen、固定长度与事务边界
上一篇
Python sqlite3.Blob 怎么改大字段:blobopen、固定长度与事务边界
TypeScript infer 关键字
下一篇
TypeScript infer 关键字
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • ljg-skills -
    ljg-skills
    ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
    5038次使用
  • MELO音乐 - AI 音乐生成平台,支持多模态创作能力
    MELO音乐
    MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
    4574次使用
  • UniScribe - AI 免费在线音视频转文字平台
    UniScribe
    UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
    4522次使用
  • 剧云 - 免费 AI 智能中文剧本创作平台
    剧云
    剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
    4780次使用
  • 万象有声 - AI 一站式有声内容创作平台
    万象有声
    万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
    4731次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码