TypeScript系列之--有趣理解函数类型泛型

news2024/9/22 7:23:22

函数类型

TS 定义函数类型需要定义输入参数类型和输出类型。

输出类型也可以忽略,因为 TS 能够根据返回语句自动推断出返回值类型。

function add(x:number, y:number):number {  
  return x + y
}
add(1,2)

函数没有明确返回值,默认返回 Void 类型

function welcome(): void {   
  console.log('hello')
}

函数表达式写法:

它写法有点类似于箭头函数

function greeter(fn: (a: string) => void) {
  fn("Hello, World");
}
 
function printToConsole(s: string) {
  console.log(s);
}
 
greeter(printToConsole);

interface 描述函数类型

interface ISum {   
   (x:number,y:number):number
}
const add:ISum = (num1, num2) => {   
  return num1 + num2
}

type 描述函数

type addType = (num1:number,num2:number) => number

可选参数

参数后加个问号,代表这个参数是可选的

function add(x:number, y:number, z?:number):number {  
  return x + y
}
add(1,2,3)
add(1,2)

注意可选参数要放在函数入参的最后面,不然会导致编译错误

默认参数

跟 JS 的写法一样,在入参里定义初始值。

和可选参数不同的是,默认参数可以不放在函数入参的最后面。但如果带默认值的参数不是最后一个参数,用户必须明确的传入 undefined值来获得默认值。

function add(x:number = 100, y:number):number {   
  return x + y
} 
add(100)
//看上面的代码,add 函数只传了一个参数,
//如果理所当然地觉得 x 有默认值,只传一个就传的是 y 的话,就会报错,
//编译器会判定你只传了 x,没传 y

add(undefined,100) // OK

泛型

TS 文档中对泛型的介绍:
 

软件工程的一个重要部分就是构建组件,组件不仅需要有定义良好和一致的 API,也需要是可复用的(reusable)。好的组件不仅能够兼容今天的数据类型,也能适用于未来可能出现的数据类型,这在构建大型软件系统时会给你最大的灵活度。

在比如 C# 和 Java 语言中,用来创建可复用组件的工具,我们称之为泛型(generics)。利用泛型,我们可以创建一个支持众多类型的组件,这让用户可以使用自己的类型消费(consume)这些组件。

这***说的简直不是人话,简直就是听君一席话,胜似一席话。。。

泛型基本使用

泛型的语法是 <> 里写类型参数,一般可以用 T 来表示;

function print<T>(arg:T):T {   
  console.log(arg)  
  return arg
}

这里的T就好似一个变量,他是什么类型由用户使用的时候传入, 通过泛型我们就能做到了输入和输出的类型统一,且可以输入输出任何类型。

泛型就是对类型编程

本质上,泛型可以理解为一个类型层面的函数,当我们指定具体的输入类型时,得到的结果是经过处理后的输出 类型 ,平时我们都是对值进行编程,泛型是对类型进行编程。

举一个例子:假如我们定义了一个 Person 类型,这个 Person 类有三个属性,并且都是必填的 ,但现在我想把它三个属性变成选填的,我们不可能重新写一个(重新写not elegant), 那么对类型的操作就剩两种了 :一种是集合操作,另一种是今天的泛型

interface Persion {
  name: string;
  age: number;
  height: number; 
}

先看集合操作:

interface Persion {
  name: string;
  age: number;
  height: number;
}
type optionPersion = Persion & {
  name?: string;
}
const ikun:optionPersion = {
  age: 36,
  height: 36
}//报错 类型 "Persion" 中需要该name属性

interface Persion {
  name?: string;
} //报错  后续属性声明必须属于同一类型。属性“name”的类型必须为“string”

似乎集合操作做不到这一点呀 ! 假如我们可以像操作函数那样操作类型,是不是有可能呢?比如我定义了一个函数 Partial,这个函数的功能入参是一个类型,返回值是新的类型,这个类型里的属性全部变成可选的 ,如下:

function Partial(Type) {
    type objType =  空类型
    for(k in Type) {
        //将内部变为可选。。。。
      objType?[k] :objType[k]
    }
    return objType
}

type PartialedPerson = Partial(Person)

但是 上面代码随便写的,不能运行 。原因是JS无法对类型进行操作

那我们来看下泛型 Partial 的具体实现,可以看出其没有直接使用 JS 的语法,而是自己定义了一套语法

function Partial(Type) {type objType =  空类型for(k in Type) {
        //将内部变为可选。。。。
      objType?[k] :objType[k]
    }return objType}

type Partial<T> = { 
  [P in keyof T]?: T[P]
};

先不管别的,我们就看看他们有多像吧!

  • 从外表看只不过是 function 变成了 type,() 变成了 <>而已。
  • 从语法规则上来看, 函数内部对标的是 ES 标准。而泛型对应的是 TS 实现的一套标准

再来看个例子:

function ids<T, U>(ary1: T, arg2: U): [T, U] {
  return [arg1, arg2];
}

泛型种类

  1. 接口泛型
interface id<T, U> {
  id1: T;
  id2: U;
}
  1. 类泛型
class MyComponent extends React.Component<Props, State> {
   ...
}

泛型约束

假设现在有这么一个函数,打印传入参数的长度,我们这么写:

function printLength<T>(arg: T): T { 
    console.log(arg.length)  
    return arg
}

因为不确定 T 是否有 length 属性,会报错。可以和 interface 结合,使用extends关键字来约束泛型

  interface ILength {  
    length: number
 }
 function printLength<T extends ILength>(arg: T): T {  
    console.log(arg.length)  
     return arg
  }
const str = printLength('lin') // 我们定义的变量一定要有 length 属性,才可以通过 TS 编译

默认参数

可以像JS函数一样给泛型加一个默认值,如下

type A<T = string> = Array<T>;
const aa: A = [1]; // type 'number' is not assignable to type 'string'.
const bb: A = ["1"]; // ok
const cc: A<number> = [1]; // ok

什么时候用泛型

当你的函数,接口或者类:

  • 需要作用到很多类型的时候,比如上面print的泛型声明。
  • 需要被用到很多地方的时候,比如Partial 泛型。

泛型支持函数嵌套

type CutTail<Tuple extends any[]> = Reverse<CutHead<Reverse<Tuple>>>

如上代码 ,Reverse 是将参数列表反转,CutHead 是将数组第一项切掉。因此 CutTail 的意思就是将传递进来的参数列表反转,切掉第一个参数,然后反转回来。具体实现有点复杂,但知道泛型支持嵌套就够了。具体参考https://zhuanlan.zhihu.com/p/147248333

泛型支持递归

泛型甚至可以嵌套自己从而形成递归,比如单链表的定义就是递归的。

type ListNode<T> = {
  data: T;
  next: ListNode<T> | null;
};

再比如 HTMLElement 的定义。

declare var HTMLElement: {
    prototype: HTMLElement;
    new(): HTMLElement;
};

我们再来看一个更复杂一点的递归形式 - 递归调用,这个递归调用的功能是:递归地将类型中所有的属性都变成 可选。类似于深拷贝那样,只不过这不是拷贝操作,而是变成可选,并且是作用在类型,而不是值。

type DeepPartial<T> = T extends Function
  ? T
  : T extends object
  ? { [P in keyof T]?: DeepPartial<T[P]> }
  : T;

type PartialedWindow = DeepPartial<Window>; // 现在window 上所有属性都变成了可选啦

常见TS 泛型工具及实现

Partial:

功能是将类型的属性变成可选。注意这是浅 Partial,DeepPartial 上面提过,只要配合递归调用使用即可。

type Partial<T> = { [P in keyof T]?: T[P] };

Required:

功能和Partial 相反,是将类型的属性变成必填, 这里的 -指的是去除。 -? 意思就是去除可选,也就是必填。。。。

type Required<T> = { [P in keyof T]-?: T[P] };

Mutable:

功能是将类型的属性变成可修改,这里的 -指的是去除。 -readonly 意思就是去除只读,也就是可修改。。。

type Mutable<T> = {
  -readonly [P in keyof T]: T[P];
};

Readonly:

功能和Mutable 相反,功能是将类型的属性变成只读, 在属性前面增加 readonly 意思会将其变成只读。

type Readonly<T> = { readonly [P in keyof T]: T[P] };

ReturnType:

功能是用来得到一个函数的返回值类型。

type ReturnType<T extends (...args: any[]) => any> = T extends (
  ...args: any[]
) => infer R
  ? R
  : any;

下面的示例用 ReturnType 获取到 Func 的返回值类型为 string,所以,foo 也就只能被赋值为字符串了

type Func = (value: number) => string;

const foo: ReturnType<Func> = "1";

有帮助到你点个赞再走吧!

参考资料:

函数_TypeScript中文文档

https://zhuanlan.zhihu.com/p/64446259

TypeScript: Documentation - Utility Types

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

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

相关文章

flink车联网项目:业务实现2(维表开发)(第68天)

系列文章目录 3.2 维表开发 3.2.1 创建库 3.2.2 示例 3.2.2.1 类型转换 3.2.2.2 创建mysql映射表 3.2.2.3 创建paimon映射表 3.2.2.4 从mysql插入到paimon表 3.2.2.5 结果查看 3.2.2.6 测试 3.2.3 其他表开发 3.2.4 部署 文章目录 系列文章目录前言3.2 维表开发3.2.1 创建库3.…

C:每日一练:单身狗(2.0版本)

前言&#xff1a; 今天在刷题的时候突然看到一道题&#xff0c;疑似一位故题。仔细一看&#xff0c;欸&#xff01;这不是就是单身狗的升级版吗&#xff1f;我想那必须再安排一篇&#xff0c;不过由于本篇文章与上一篇单身狗文章所涉及的知识点基本相同&#xff0c;所以还请大…

小型企业客户关系管理系统pf

TOC springboot457小型企业客户关系管理系统pf 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0…

推送本地windows环境镜像到阿里云镜像仓库

说明&#xff1a;从dockerhub拉取了apache/kafka3.7.0镜像到本地windwos操作系统上&#xff0c;再将该镜像推送到阿里云镜像仓库&#xff0c;记录了本次操作过程。 1、启动本地的docker desktop&#xff0c;搜索官方镜像 将搜索到的apache/kafka官方镜像拉取到本地 镜像拉取…

代码随想录算法训练营_day18

题目信息 530. 二叉搜索树的最小绝对差 题目链接: https://leetcode.cn/problems/minimum-absolute-difference-in-bst/description/题目描述: 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&#xff0c;其数值等…

SpringBoot基础(二):配置文件详解

SpringBoot基础系列文章 SpringBoot基础(一)&#xff1a;快速入门 SpringBoot基础(二)&#xff1a;配置文件详解 目录 一、配置文件分类二、配置文件优先级1、不同版本优先级2、不同位置优先级 三、配置文件格式1、yml和yaml格式1.1、字符串1.2、布尔类型1.3、整数型1.4、浮点…

论文阅读笔记:The Graph Neural Network Model

论文来源 IEEE Transactions on Neural Networks&#xff0c;Volume: 20 Issue: 1 背景 图神经网络模型本身具有广泛的使用背景&#xff0c;由于我个人研究交通流量预测的需要&#xff0c;此处仅考虑深度学习领域。图结构指的是由节点node和若干个连接的边edge组成的一种数据…

【Docker】Docker Compose(容器编排)

一、什么是 Docker Compose docker-compose 是 Docker 官方的开源项目&#xff0c;使用 python 编写&#xff0c;实现上调用了 Docker 服务的 API 进行容器管理及编排&#xff0c;其官方定义为定义和运行多个 Docker 容器的应用。 docker-compose 中有两个非常重要的概念&…

关于Python3项目中依赖包管理问题

背景&#xff1a;最近在使用Python3.11编写脚本来获取google play中app的用户评论&#xff0c;脚本中需要安装多个依赖包&#xff0c;在本地Pycharm调试通过以后&#xff0c;上传到github&#xff0c;然后在linux服务器拉取脚本来运行&#xff0c;发现存在几个问题。本文将面临…

【海贼王航海日志:前端技术探索】一篇文章带你走进JavaScript(三)

目录 1 -> WebAPI背景知识 1.1 -> 什么是WebAPI 1.2 -> 什么是API 1.3 -> 什么是DOM 1.3.1 -> DOM树 2 -> 获取元素 2.1 -> querySelector 2.2 -> querySelectorAll 3 -> 事件初识 3.1 -> 基本概念 3.2 -> 事件三要素 4 -> 操…

Apache Tomcat 信息泄露漏洞CVE-2024-21733、CVE-2024-24549和CVE-2024-34750排查处理

一、漏洞描述 Apache Tomcat作为一个流行的开源Web服务器和Java Servlet容器并用于很多中小型项目的开发中。其中,Coyote作为Tomcat的连接器组件,是Tomcat服务器提供的供客户端访问的外部接口,客户端通过Coyote与服务器建立链接、发送请求并且接收响应。 近日发现Apache To…

政企单位如何选择适合规模的即时通讯软件?

政企单位在不同规模的组织结构中都面临着沟通和协作的挑战。为了提高工作效率和团队协作能力&#xff0c;选择适合规模的即时通讯软件至关重要。本文将为政企单位在选择适合规模的即时通讯软件时提供一些关键要素和指导&#xff0c;同时重点介绍WorkPlus作为一个可以迎合政企单…

Java语言程序设计——篇十四(1)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f333;&#x1f333;&…

34_Web漏洞扫描工具、常见Web漏洞扫描工具、AWVS的部署与使用、 渗透测试执行流程、AWVS破解

Web漏洞扫描工具 Web漏洞扫描是在Web信息收集的基础上&#xff0c;进行更进一步的自动化的安全评估、漏洞挖掘、渗透测试 Web漏洞扫描会出现漏报&#xff0c;需要手工结合使用 常见Web漏洞扫描工具 AWVS、OWASP ZAP、Arachni、Nitko、Paros... 渗透测试执行流程&#xff1a…

IDEA研究院编程语言MoonBit发布beta预览版,快速实现多领域应用

MoonBit beta 预览版比大部分主流语言更早推出现代化泛型、精准错误处理和高效迭代器等重要特性&#xff0c;在云计算、边缘计算、人工智能和教育等领域快速实现落地应用。Beta 预览版标志着 MoonBit 生态进入全新阶段&#xff0c;为用户提供更稳定、流畅的创新操作体验。 Moo…

C++入门——03内存管理

上图为C语言的内存管理&#xff0c;C中可以继续使用&#xff0c;但有些地方就无能为力而且使用起来比较麻烦&#xff0c;因此C又提出了自己的内存管理方式&#xff1a;通过new和delete操作符进行动态内存管理。 1.new和delete操作符 1.1.new/delete操作内置类型 注意&#xf…

自闭症青年的行为特征有哪些

自闭症&#xff0c;又称孤独症&#xff0c;是一种神经发育障碍&#xff0c;它不仅影响儿童的成长&#xff0c;也会在青年时期展现出一系列独特的行为特征。了解这些特征对于更好地支持和帮助自闭症青年融入社会至关重要。 社交互动方面的困难是自闭症青年较为显著的特征之一。他…

IO进程(学习)2024.8.17

目录 文件属性获取 目录操作 标准IO 和文件IO 的区别 库 库的定义 库的分类 静态库 动态库 库的制作 制作静态库 动态库的制作 使用库 进程 程序和进程的区别 程序&#xff1a;编译好的可执行文件 进程&#xff1a;一个独立的可调度的任务 特点 进程段 进程…

SMS流媒体服务器-MPEG-PS流的深度解析

1.简介 ps流的解析&#xff0c;只要按照标准文档对照16进制的流数据&#xff0c;基本都能看的明白。但是实际项目中会碰到各种各样的问题。本文将对如何高效的解析出音视频数据发表一下个人的看法。 介绍一下本人的开源流媒体&#xff0c;点个star&#xff0c;有兴趣一起开发的…

应急响应:勒索病毒-实战 案例一.【Windows 系统-排查和解密】

什么是勒索病毒. 勒索病毒是一种恶意软件&#xff0c;它通过加密用户的数据或锁定用户设备&#xff0c;然后要求用户支付赎金以解锁数据或系统。勒索病毒的入侵方式多样&#xff0c;包括网络共享文件、捆绑传播、垃圾邮件、水坑攻击、软件供应链传播、暴力破解、利用已知漏洞攻…