【TS】TypeScript函数类型:提升函数的类型安全性和可读性


🌈个人主页: 鑫宝Code
🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础
💫个人格言: "如无必要,勿增实体"
文章目录
- TypeScript函数类型:提升函数的类型安全性和可读性
- 1. 引言
- 2. 基本函数类型
- 2.1 函数声明
- 2.2 函数表达式
- 2.3 箭头函数
- 3. 可选参数和默认参数
- 3.1 可选参数
- 3.2 默认参数
- 4. 剩余参数
- 5. 函数重载
- 6. 泛型函数
- 6.1 泛型约束
- 7. 函数类型接口
- 8. 回调函数类型
- 9. 构造函数类型
- 10. 函数类型推断
- 11. 高级类型与函数
- 11.1 联合类型
- 11.2 交叉类型
- 12. 条件类型与函数
- 13. 函数类型的最佳实践
- 14. 常见陷阱和解决方案
- 14.1 this的类型
- 14.2 回调函数中的this
- 15. 实际应用示例
- 16. 结论
TypeScript函数类型:提升函数的类型安全性和可读性
1. 引言
在JavaScript中,函数是一等公民,它们可以被赋值给变量、作为参数传递,甚至作为其他函数的返回值。TypeScript作为JavaScript的超集,不仅继承了这些特性,还为函数添加了强大的类型系统支持。本文将深入探讨TypeScript中的函数类型,包括其定义、使用方法以及高级特性,帮助您更好地在TypeScript项目中使用函数,提高代码的类型安全性和可读性。
2. 基本函数类型
2.1 函数声明
在TypeScript中,我们可以为函数的参数和返回值添加类型注解:
function add(x: number, y: number): number {return x + y;
}
2.2 函数表达式
函数表达式也可以添加类型注解:
const multiply: (x: number, y: number) => number = function(x, y) {return x * y;
};
2.3 箭头函数
箭头函数同样支持类型注解:
const divide = (x: number, y: number): number => x / y;
3. 可选参数和默认参数
3.1 可选参数
使用?
标记可选参数:
function greet(name: string, greeting?: string): string {return greeting ? `${greeting}, ${name}!` : `Hello, ${name}!`;
}
3.2 默认参数
默认参数可以与类型注解结合使用:
function createUser(name: string, age: number = 18): { name: string, age: number } {return { name, age };
}
4. 剩余参数
TypeScript支持剩余参数,并可以为其指定类型:
function sum(...numbers: number[]): number {return numbers.reduce((acc, curr) => acc + curr, 0);
}
5. 函数重载
TypeScript允许我们为同一个函数提供多个函数类型定义:
function reverse(x: string): string;
function reverse(x: number[]): number[];
function reverse(x: string | number[]): string | number[] {if (typeof x === 'string') {return x.split('').reverse().join('');} else {return x.slice().reverse();}
}
6. 泛型函数
泛型允许我们在定义函数时不指定具体的类型,而在使用时再指定:
function identity<T>(arg: T): T {return arg;
}let output = identity<string>("myString");
6.1 泛型约束
我们可以使用extends关键字来约束泛型类型:
interface Lengthwise {length: number;
}function loggingIdentity<T extends Lengthwise>(arg: T): T {console.log(arg.length); // Now we know it has a .length propertyreturn arg;
}
7. 函数类型接口
我们可以使用接口来描述函数类型:
interface MathFunc {(x: number, y: number): number;
}let add: MathFunc = (x, y) => x + y;
let subtract: MathFunc = (x, y) => x - y;
8. 回调函数类型
在处理回调函数时,正确定义其类型非常重要:
function fetchData(callback: (data: string) => void): void {// 模拟异步操作setTimeout(() => {callback("Data fetched");}, 1000);
}
9. 构造函数类型
TypeScript允许我们定义构造函数的类型:
interface Point {x: number;y: number;
}interface PointConstructor {new (x: number, y: number): Point;
}function createPoint(ctor: PointConstructor, x: number, y: number): Point {return new ctor(x, y);
}
10. 函数类型推断
TypeScript能够根据上下文自动推断函数的类型:
let numbers = [1, 2, 3, 4, 5];
numbers.forEach((num) => {console.log(num.toFixed(2)); // TypeScript knows 'num' is a number
});
11. 高级类型与函数
11.1 联合类型
函数可以接受或返回联合类型:
function formatValue(value: string | number): string {if (typeof value === "string") {return value.toUpperCase();}return value.toFixed(2);
}
11.2 交叉类型
交叉类型可以用来组合多个函数类型:
type Adder = (a: number, b: number) => number;
type Multiplier = (a: number, b: number) => number;type Calculator = Adder & Multiplier;const calc: Calculator = (a, b) => a + b; // 或 a * b
12. 条件类型与函数
条件类型可以根据条件选择不同的函数类型:
type FunctionType<T> = T extends string ? (arg: string) => string : (arg: number) => number;function processValue<T extends string | number>(value: T): FunctionType<T> {if (typeof value === "string") {return ((arg: string) => arg.toUpperCase()) as FunctionType<T>;} else {return ((arg: number) => arg * 2) as FunctionType<T>;}
}
13. 函数类型的最佳实践
-
明确指定返回类型:虽然TypeScript可以推断返回类型,但明确指定可以提高代码可读性和可维护性。
-
使用函数类型别名:对于复杂的函数类型,使用类型别名可以提高可读性:
type AsyncCallback<T> = (error: Error | null, result: T) => void;
-
利用泛型:当函数可以处理多种类型时,优先考虑使用泛型而不是any。
-
使用函数重载:当函数根据不同的参数有不同的行为时,使用函数重载可以提供更精确的类型信息。
-
避免过度使用可选参数:过多的可选参数可能导致函数调用变得复杂,考虑使用对象参数模式。
14. 常见陷阱和解决方案
14.1 this的类型
在TypeScript中,可以明确指定this的类型:
interface User {name: string;greet(this: User): void;
}const user: User = {name: "Alice",greet() {console.log(`Hello, I'm ${this.name}`);}
};
14.2 回调函数中的this
在回调函数中,this的类型可能会丢失。使用箭头函数或显式绑定可以解决这个问题:
class Handler {info: string;onEvent(this: Handler, e: Event) {// this的类型是Handlerthis.info = e.type;}
}
15. 实际应用示例
让我们通过一个实际的应用示例来展示TypeScript函数类型的强大功能:
// 定义一个表示HTTP方法的类型
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';// 定义一个表示API端点的接口
interface ApiEndpoint<T> {url: string;method: HttpMethod;params?: object;response: T;
}// 定义一个通用的API调用函数
async function apiCall<T>(endpoint: ApiEndpoint<T>): Promise<T> {const { url, method, params } = endpoint;const response = await fetch(url, {method,body: params ? JSON.stringify(params) : undefined,headers: {'Content-Type': 'application/json'}});if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}return await response.json() as T;
}// 定义一些具体的API端点
interface User {id: number;name: string;email: string;
}const getUserEndpoint: ApiEndpoint<User> = {url: '/api/user/1',method: 'GET',response: {} as User
};const createUserEndpoint: ApiEndpoint<User> = {url: '/api/user',method: 'POST',params: { name: 'New User', email: 'newuser@example.com' },response: {} as User
};// 使用apiCall函数
async function fetchUser() {try {const user = await apiCall(getUserEndpoint);console.log('Fetched user:', user.name);} catch (error) {console.error('Error fetching user:', error);}
}async function createUser() {try {const newUser = await apiCall(createUserEndpoint);console.log('Created user:', newUser.id);} catch (error) {console.error('Error creating user:', error);}
}fetchUser();
createUser();
这个例子展示了如何使用TypeScript的函数类型和泛型来创建一个类型安全的API调用系统:
- 使用类型别名和接口定义API相关的类型
- 创建一个泛型函数
apiCall
来处理不同类型的API请求 - 使用函数重载和条件类型可以进一步改进这个系统,以处理更复杂的场景
16. 结论
TypeScript的函数类型系统为JavaScript的函数添加了强大的类型检查和安全性。通过本文的介绍,我们探讨了从基本的函数类型定义到高级特性如泛型、条件类型等多个方面。掌握这些概念和技巧,将帮助您更有效地使用TypeScript,编写出更加健壮、可维护的代码。
在实际开发中,合理运用函数类型可以大大减少运行时错误,提高代码质量。随着您在项目中不断实践,您会发现TypeScript的函数类型系统不仅能捕获潜在的错误,还能提供更好的代码提示和自动完成功能,从而提高开发效率。
继续探索和实践,相信您会在TypeScript的类型系统中发现更多精彩,让您的函数更加安全、清晰和高效!

相关文章:

【TS】TypeScript函数类型:提升函数的类型安全性和可读性
🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 💫个人格言: "如无必要,勿增实体" 文章目录 TypeScript函数类型:提升函数的类型安全性和可读性1. 引言2. 基本函…...
“八股文”在实际工作中是助力、阻力还是空谈?
前言:在当今快速发展的技术时代,程序员的角色变得日益重要。随着技术的不断进步,招聘流程也在不断演变以适应新的需求。在程序员的招聘过程中,“八股文”作为一种面试现象,已成为不可忽视的一部分。所谓“八股文”&…...

代码随想录算法训练营第22天-leetcode-回溯算法part01:
#回溯算法理论基础 能解决的问题: 组合问题:N个数里面按一定规则找出k个数的集合切割问题:一个字符串按一定规则有几种切割方式子集问题:一个N个数的集合里有多少符合条件的子集排列问题:N个数按一定规则全排列&…...

MySql 触发器、存储器练习
一: 触发器 1、建立两个表:goods(商品表)、orders(订单表) 查看数据库:mysql> show databases; 使用数据库:mysql> use mydb16_trigger; 创建goods表: mysql> create table goods(gid char(8) not null primary key, …...

【Plotly-驯化】一文教您画出Plotly中动态可视化饼图:pie技巧
【Plotly-驯化】一文教您画出Plotly中动态可视化饼图:pie技巧 本次修炼方法请往下查看 🌈 欢迎莅临我的个人主页 👈这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合,智慧小天地! 🎇 免费获取相关内…...

Mirror学习笔记(一) 简介
文章目录 一、常规学习:Mirror核心功能有服务器和主机 二、时间戳批处理时间戳 三、TCP和UDP四、CCU(同时在线人数)五、SyncDirection(同步方向)六、RTT(往返时间)七、Connection Quality(连接质量)八、Lag Compensati…...

终端pip安装包后,Pycharm却导入失败?新手别慌,3招搞定!
很多小伙伴在学习Python的过程中,都会遇到这种情况:明明在终端用pip安装好了需要的包,但在Pycharm中导入时却报错。难道是安装姿势不对? 例如在cmd中已经有了pandas,但是去pycharm中导入pandas显示没有 先别急着怀疑人生,这很可能是因为pip安装包的路径和Pycharm项目使用…...

Redis 与 Scrapy:无缝集成的分布式爬虫技术
1. 分布式爬虫的概念 分布式爬虫系统通过将任务分配给多个爬虫节点,利用集群的计算能力来提高数据抓取的效率。这种方式不仅可以提高爬取速度,还可以在单个节点发生故障时,通过其他节点继续完成任务,从而提高系统的稳定性和可靠性…...

大厂linux面试题攻略四之Linux网络服务(一)
一、Linux网络服务-SSH服务 1.哪些设置能够提升SSH远程管理的安全等级? ssh的登录验证方式 ssh的登录端口和监听设置: 配置文件: /etc/ssh/sshd_config #Port 22 #ssh服务默认监听端口 #ListenAddress 0.0.0.0 #ssh服务…...
【Pulling fs layer】Linux使用docker-compose的时候,一直Pulling fs layer
当Docker在拉取镜像时卡在“pulling fs layer”阶段,可以通过重启Docker服务来解决。 具体步骤如下: 首先,尝试重启Docker服务。可以通过运行以下命令来重启Docker服务: systemctl restart docker 这个命令会重启Docker服务…...

最新保姆级教程使用WildCard开通Claude3升级ChatGPT4.0(2024.8)
如何使用 WildCard 服务注册 Claude3 随着 Claude3 的震撼发布,最强 AI 模型的桂冠已不再由 GPT-4 独揽。Claude3 推出了三个备受瞩目的模型:Claude 3 Haiku、Claude 3 Sonnet 以及 Claude 3 Opus,每个模型都展现了卓越的性能与特色。其中&a…...

layui 乱入前端
功能包含 本实例代码为部分傻瓜框架,插入引用layui。因为样式必须保证跟系统一致,所以大部分功能都是自定义的。代码仅供需要用layui框架,但原项目又不是layui搭建的提供解题思路。代码较为通用 自定义分页功能自定义筛选列功能行内编辑下拉、…...

中国十大顶级哲学家,全球公认的伟大思想家颜廷利:人类为何拥有臀部
人类为何拥有臀部?若众生皆无此部位,又如何能寻得一处真正属于自己的“座位”?在博大精深的中国传统文化中,汉字“座”与“坐”均蕴含“土”字元素。在易经的智慧里,作为五行之一的“土”,象征着人类社会的…...

Threejs中导入GLTF模型克隆后合并
很多场景中会需要同一个模型很多次,但是如果多次加载同一个模型会占用很高的带宽,导致加载很慢,因此就需要使用clone,也就是加载一个模型后,其他需要使用的地方使用clone的方式复制出多个同样的模型,再改变…...

今日arXiv最热大模型论文:北京大学最新综述:视觉大模型中的漏洞与攻防对抗
近年来,视觉语言大模型(LVLM)在文本转图像、视觉问答等任务中大放异彩,背后离不开海量数据、强大算力和复杂参数的支撑。 但是!大模型看似庞大的身躯背后却有一颗脆弱的“心脏”,极易受到攻击。攻击者可以…...

为什么IDEA中使用@Autowired会被警告
我们在使用IDEA编码时,如果用到了Autowired注解注入bean,会发现IDEA会给代码标个波连线,鼠标移动上去,会发下idea提示:不推荐使用Filed injection,这是Spring的核心DI(Dendency Injection&#…...
uniapp使用cover-view,使用@click无效
最近要做直播详情页面,用的是第三方直播链接,需要在该页面上放两个按钮,点击按钮需要弹出相关商品及优惠券。类似于抖音直播页面。 第三方链接使用的是web-view进行展示。由于该组件优先级太高,正常的前端组件无法在该页面浮现展…...

Postman 接口测试工具简易使用指南
一、Postman是什么? 我通过kimi问了这样一个问题,它给我的回答是这样的: 它的回答也算比较中规中矩,简单的说postman实际上就是一款接口测试工具,同时它还可以编写对应的测试脚本以及自动生成对应的API文档,结合我的习惯来说&am…...

Move生态:从Aptos和Sui到Starcoin的崛起
区块链技术自诞生以来,已经经历了多个发展阶段和技术迭代。近年来,随着智能合约平台的不断演进,以Move语言为核心的生态系统逐渐崭露头角。Move语言以其安全性、灵活性和高效性吸引了大量开发者和项目方的关注。在Move生态中,Apto…...
MacOS DockerDesktop配置文件daemon.json的位置
如果因为通过可视化页面修改配置错误导致客户端启动不起来,可以去找对应的配置文件通过 vim 修改后重启客户端 cd ~/.docker/...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...

【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...
CppCon 2015 学习:Time Programming Fundamentals
Civil Time 公历时间 特点: 共 6 个字段: Year(年)Month(月)Day(日)Hour(小时)Minute(分钟)Second(秒) 表示…...

Java中HashMap底层原理深度解析:从数据结构到红黑树优化
一、HashMap概述与核心特性 HashMap作为Java集合框架中最常用的数据结构之一,是基于哈希表的Map接口非同步实现。它允许使用null键和null值(但只能有一个null键),并且不保证映射顺序的恒久不变。与Hashtable相比,Hash…...