C++20中的Concepts与TypeScript

C++20中的Concepts与TypeScript

大家好!上一篇聊了C++20中概念(Concepts),这是一个非常赞的特性,极大简化了模板编程,但是如果跳出C++去查看一下其他编程语言的特性,就会发现,这样类似的特性其实早就已经有了。语言是相通的,相互借鉴的,所以呈现出越来越像的趋势。

  • Java:通过泛型(Generics)和接口(Interfaces),以及extends关键字进行类型约束。
  • C#:通过泛型(Generics)和接口(Interfaces),以及where关键字进行类型约束。
  • TypeScript:通过泛型(Generics)和extends关键字进行类型约束。

这些语言特性都可以用于定义和约束泛型参数,确保类型安全,同时提高代码的可读性和可维护性。每个语言有其独特的语法和实现方式,但在本质上,它们都为开发者提供了强大的类型检查和约束能力。

今天我们来聊聊在TypeScript中如何实现类似C++20中概念(Concepts)的功能。虽然TypeScript没有直接等同于C++20的Concepts,但我们可以通过泛型(Generics)和类型守卫(Type Guards)来实现类似的类型约束(Type Constraints。接下来,我们通过几个具体的例子来展示如何在TypeScript中实现这些约束。

c++-concept-ts

什么是泛型?

泛型这个概念,其实挺简单,就是让我们的函数、接口或者类能够处理多种类型的数据,而不是被限制在一种特定类型上。可以这么理解,就是你写了一个方法,它能够根据你传入的数据类型,自己“变”成去处理这种类型的方法。

举个例子,假如我们有一个函数,它的作用是返回传进去的参数。传统的写法,你可能写:

function identity(arg: number): number {return arg;
}

但这样我们只能处理 number 类型的数据。那我们要处理 string 类型呢?要再写一个函数吗?当然不是,我们可以用泛型来解决:

function identity<T>(arg: T): T {return arg;
}

看到了吧,我们给函数名后面加了 <T>,T 代表一种类型,这个类型是传进函数的时候才决定的。所以我们可以这样用:

console.log(identity<number>(123)); // 输出 123
console.log(identity<string>("Hello")); // 输出 Hello

泛型如何使用?

泛型不仅可以用在函数上,还可以用在接口和类上。

泛型接口

有时候,我们希望用接口来定义某个数据类型的集合,比如:

interface GenericIdentityFn<T> {(arg: T): T;
}let myIdentity: GenericIdentityFn<number> = identity;
console.log(myIdentity(123)); // 输出 123

泛型类

我们也可以把泛型用在类上:

class GenericNumber<T> {zeroValue: T;add: (x: T, y: T) => T;
}let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = (x, y) => x + y;
console.log(myGenericNumber.add(1, 2)); // 输出 3

类型约束

有时候,我们希望泛型不仅仅是任意一种类型,而是某种特定类型的子类型,这时候我们就要用到类型约束了。举个例子,我们希望泛型参数一定要有 length 属性:

interface Lengthwise {length: number;
}function loggingIdentity<T extends Lengthwise>(arg: T): T {console.log(arg.length); // 这里可以访问 length 属性,因为我们约束了 T 必须有 lengthreturn arg;
}loggingIdentity({length: 10, value: "Hello"}); // 没问题
// loggingIdentity(123); // 报错:因为 number 类型没有 length 属性

这里我们用 extends 关键字约束 T 必须有 length 属性,所以传入的参数必须是一个带有 length 属性的对象。

泛型如何实现类似于C++20中的概念

让我们来看一下如何在TypeScript中对泛型进行约束,以确保它们满足特定的条件,类似于C++20中的概念。

示例1:检查类型是否支持+运算符

由于TypeScript不支持运算符重载,我们可以通过接口来确保给定的类型具有特定的方法,从而模拟我们需要的行为。

interface Addable {valueOf(): number;
}function add<T extends Addable>(a: T, b: T): number {return a.valueOf() + b.valueOf();
}console.log(add(1, 2));         // 正常:3
console.log(add(new Number(1.5), new Number(2.5)));  // 正常:4

在这个例子中:

  • Addable接口确保类型必须有一个valueOf方法并返回一个数字。
  • add函数使用泛型并限制为T extends Addable

但是对于如下代码,

console.log(add("a", "b"));  

将会产生这样的编译错误,应该还是非常清晰和容易理解的。

error TS2345: Argument of type '"a"' is not assignable to parameter of type 'Addable'.

示例2:检查类型是否具有length属性

我们创建一个函数,检查类型是否有length属性并且是可迭代的:

interface HasLength {length: number;
}function printLength<T extends HasLength>(item: T): void {console.log(item.length);
}printLength("Hello");  // 正常:5
printLength([1, 2, 3, 4]);  // 正常:4
// 

在这个例子中:

  • HasLength接口确保类型必须有一个length属性。
  • printLength函数使用泛型并限制为扩展自HasLength的类型。

但是对于如下代码,

printLength(123); 

将会产生这样的编译错误

error TS2345: Argument of type '123' is not assignable to parameter of type 'HasLength'.

示例3:类型守卫检查可迭代性

TypeScript的类型守卫允许你创建在运行时执行检查的函数,从而缩小类型范围。这些检查能提供类似Concepts的高级验证。为了确保一个类型是可迭代的,我们可以创建一个类型守卫函数:

type IterableType = {[Symbol.iterator](): Iterator<any>;
}function isIterable<T>(obj: T): obj is T & IterableType {return typeof (obj as any)[Symbol.iterator] === 'function';
}function printAll<T>(container: T): void {if (isIterable(container)) {for (const item of container) {console.log(item);}} else {console.log("不可迭代");}
}printAll([1, 2, 3]);  // 正常:1 2 3
printAll("Hello");  // 正常:H e l l o
// printAll(123);  // 正常:不可迭代

在这个例子中:

  • isIterable函数检查一个对象是否具有[Symbol.iterator]方法,从而判断其可迭代性。
  • printAll函数仅在对象可迭代时执行迭代操作。

示例4:组合多种约束

我们可以使用类型守卫组合多种约束,确保类型满足多个条件:

interface HasBegin {begin(): void;
}interface HasEnd {end(): void;
}type CompleteType = HasBegin & HasEnd;function hasBegin<T>(obj: T): obj is T & HasBegin {return typeof (obj as any).begin === 'function';
}function hasEnd<T>(obj: T): obj is T & HasEnd {return typeof (obj as any).end === 'function';
}function process<T>(obj: T): void {if (hasBegin(obj) && hasEnd(obj)) {obj.begin();obj.end();} else {console.log("对象不符合要求");}
}const validObject = {begin: () => console.log("Begin"),end: () => console.log("End")
};const invalidObject = {begin: () => console.log("Begin")
};process(validObject);  // 正常:Begin End
process(invalidObject);  // 正常:对象不符合要求

在这个例子中:

  • CompleteType表示具有beginend方法的类型。

  • process函数使用类型守卫hasBeginhasEnd确保对象满足要求。

总结

C++-is-a-general-purpose-programming-language

虽然TypeScript没有完全等同于C++20的Concepts,但通过TypeScript的类型系统、泛型和类型约束,可以实现类似的功能。定义接口、使用类型约束,我们可以确保传递给函数的类型满足特定的条件,从而使代码更加健壮和类型安全。

希望这些示例能帮助你更好地理解如何在TypeScript中实现类似C++20 Concepts的功能,同时,又可以让你对C++20 Concepts有更深入的理解。如果有任何问题或者需要进一步的帮助,欢迎随时提问!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/19183.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

联想thinkpad笔记本哪些配置可以安装win7_联想thinkpad笔记本装win7解析(支持新旧机型)

联想thinkpad笔记本哪些配置可以安装win7&#xff1f;联想ThinkPad L14在安装win7后usb键盘不能使用&#xff0c;并且bios中要关闭安全启动和开启CSM兼容模式&#xff0c;那么联想ThinkPad L14要怎么安装win7系统呢&#xff1f;下面小编就给大家介绍详细的联想ThinkPad L14装wi…

IDEA如何设置编码格式,字符编码,全局编码和项目编码格式

前言 大家好&#xff0c;我是小徐啊。我们在开发Java项目&#xff08;Springboot&#xff09;的时候&#xff0c;一般都是会设置好对应的编码格式的。如果设置的不恰当&#xff0c;容易造成乱码的问题&#xff0c;这是要避免的。今天&#xff0c;小徐就来介绍下我们如何在IDEA…

实习冲刺第二十五天

283.移动零 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0] 思路详解&#xff1a…

使用QTimer和SIGNAL/SLOT机制来实现系统时间的显示

在Qt中&#xff0c;使用QTimer和SIGNAL/SLOT机制来实现系统时间的显示是一个常见的做法。下面是如何实现这一功能的步骤&#xff1a; 创建定时器&#xff1a; 首先&#xff0c;你需要创建一个QTimer对象。QTimer是一个定时器类&#xff0c;可以在指定的时间间隔后发出信号。 QT…

Win11安装软件被系统阻止安装?解除限制的方法

Windows 11作为最新的操作系统&#xff0c;加入了许多安全性和稳定性的新特性。但也因此&#xff0c;一些用户在安装软件时可能遇到“安装被阻止”或“无法从此位置安装应用程序”的提示。这通常是由于系统的默认安全设置或权限限制导致的。本文将探讨这些限制的原因&#xff0…

三角波生成函数

% 设置时间范围和采样频率 t 0:0.01:2; % 时间从0到2秒&#xff0c;步长为0.01秒% 定义频率 f 和角频率 theta f 5; % 频率为5Hz theta 2 * pi * f * t;% 初始化输出向量 y zeros(size(t));% 根据给定的公式计算 y for k 1:fy y (-1)^(k-1)*(2 /(k * pi)) * sin(k * the…

sglang 部署Qwen2VL7B,大模型部署,速度测试,深度学习

sglang 项目github仓库&#xff1a; https://github.com/sgl-project/sglang 项目说明书&#xff1a; https://sgl-project.github.io/start/install.html 资讯&#xff1a; https://github.com/sgl-project/sgl-learning-materials?tabreadme-ov-file#the-first-sglang…

『大模型笔记』AI自动化编程工具汇总!

『大模型笔记』AI自动化编程工具汇总! 文章目录 一. Bolt.new(开源AI驱动全栈Web开发工具)1.1. Bolt.new介绍1.2. 编程小白如何打造自己的导航网站二. Cursor(人工智能代码编辑器)2.1. Cursor入门教程2.2. Cursor左侧布局设置和VSCode一样一. Bolt.new(开源AI驱动全栈Web开发工…

网页全终端安防视频流媒体播放器EasyPlayer.jsEasyPlayer.js关于多屏需求

EasyPlayer.js网页全终端安防视频流媒体播放器是一款功能强大的H5播放器&#xff0c;支持多种视频协议&#xff0c;包括HTTP、HTTP-FLV、HLS&#xff08;m3u8&#xff09;、WS、WEBRTC、FMP4等&#xff0c;兼容视频直播与点播功能。同时&#xff0c;它支持多种音视频编码格式&a…

大模型外挂知识库优化——如何利用大模型辅助召回

大模型外挂知识库优化——如何利用大模型辅助召回&#xff1f; 一、为什么需要使用大模型辅助召回&#xff1f; 我们可以通过向量召回的方式从文档库里召回和用户问题相关的文档片段&#xff0c;同时输入到LLM中&#xff0c;增强模型回答质量。 常用的方式直接用用户的问题进…

three.js实现地球 外部扫描的着色器

three.js实现地球 外部扫描的着色器 https://threehub.cn/#/codeMirror?navigationThreeJS&classifyshader&idearthScan import * as THREE from three import { OrbitControls } from three/examples/jsm/controls/OrbitControls.js import { GUI } from three/ex…

STM32 BootLoader 刷新项目 (十一) Flash写操作-命令0x57

STM32 BootLoader 刷新项目 (十一) Flash写操作-命令0x57 1. 引言 嵌入式系统中&#xff0c;BootLoader 是设备启动的第一部分代码&#xff0c;负责硬件初始化和主程序加载。在 STM32F407 中&#xff0c;BootLoader 的另一重要功能是支持应用程序的在线升级&#xff0c;这需要…

Spring IoC——针对实习面试

目录 Spring IoC谈谈你对Spring IoC的理解IoC和DI有区别吗&#xff1f;IoC&#xff08;控制反转&#xff09;DI&#xff08;依赖注入&#xff09;IoC与DI的区别 什么是Spring Bean&#xff1f;作用域有哪些&#xff1f;Bean是线程安全的吗&#xff1f;说一下Spring Bean的生命周…

【H2O2|全栈】MySQL的云端部署

目录 前言 开篇语 准备工作 MySQL移除 为什么需要移除&#xff1f; 移除操作 Yum仓库 yum简介 rpm安装 yum库安装 MySQL安装 使用yum安装 开机自启动 检查运行状态 MySQL配置 初始密码 ​编辑登录 修改root密码 退出MySQL 字符集配置 结束语 前言 开篇语…

数据结构-二叉平衡树

一.平衡二叉树 二叉搜索树插入的次序不同导致不同的深度和平均查找长度ASL 左右子树高度差不超过绝对值1的二叉搜索是二叉平衡树 二.平衡二叉树的调整 在右子树的右子树上的插入做RR插入 把被破坏节点的右子树变成跟节点并把这个右子树的左子树挂载到原来被破坏的结点的右子树…

【PCIE716-0】基于PCIe总线架构的XC7Z100 FPGA高性能实时信号处理平台

板卡概述 PCIE716-0是一款基于PCIe总线架构的XC7Z100 FPGA高性能实时信号处理平台。该平台采用Xilinx的ZYNQ SOC系列产品XC7Z100作为主处理器。 该平台的PL端具有1个FMC&#xff08;HPC&#xff09;接口&#xff0c;1路PCIe x8主机接口&#xff0c;支持1路UART串口、支持1组6…

从0开始的数据结构速过——番外(1)

目录 尝试 思考与架构设置 编写&#xff01; 一些额外知识的补充 可变参数模板 std::common_type std::forward 这是《数据结构从0开始》的一个番外。实际上是介绍一下一些现代C的写法。这里以快速构建std::array作为契机来说明一下一些现代C的语法。 尝试 我们在这里呢…

Day10_CSS过度动画

Day10_CSS过度动画 背景 : PC和APP项目我们已经开发完毕, 但是再真正开发的时候有些有些简易的动态效果我们可以使用CSS完成 ; 本节课我们来使用CSS完成基础的动画效果 今日学习目标 CSS3过度CSS3平面动态效果CSS3动画效果案例 1. CSS3过渡 ​ 含义 :过渡指的是元素从一种…

如何制作代购系统的客服支持模块

在代购系统中&#xff0c;客服支持模块是维护用户满意度和忠诚度的关键环节。一个有效的客服支持模块不仅可以解决用户的疑问和问题&#xff0c;还可以收集用户反馈&#xff0c;用于改进服务和产品。本文将详细介绍如何制作一个代购系统的客服支持模块&#xff0c;包括前端界面…

【unity小技巧】一些unity3D灯光的使用与渲染及性能优化方案

文章目录 天空盒反射配置太阳耀斑眩光烘培光照烘培光照时弹出错误&#xff0c;记得勾选模型下面的选择阴影项目配置光源模型模型shader的问题 全局光照混合光照模式混合照明模式减性照明模式Shadowmask照明模式间接烘焙照明模式 环境光遮罩灯光探针反射探针技术关闭反射探针可以…