【阮一峰】5.函数

函数

简介

函数的类型声明,需要在声明函数时,给出参数的类型和返回值的类型。

function hello(txt: string): void {console.log("hello " + txt);
}

如果变量被赋值为一个函数,变量的类型有两种写法。

// 写法一
const hello = function (txt: string) {console.log("hello " + txt);
};// 写法二
const hello: (txt: string) => void = function (txt) {console.log("hello " + txt);
};

函数类型里面的参数名与实际参数名,可以不一致。

let f: (x: number) => number;f = function (y: number) {return y;
};

type 命令为函数类型定义一个别名,便于指定给其他变量。

type MyFunc = (txt: string) => void;const hello: MyFunc = function (txt) {console.log("hello " + txt);
};

函数的实际参数个数,可以少于类型指定的参数个数,但是不能多于,即 TypeScript 允许省略参数。

let myFunc: (a: number, b: number) => number;myFunc = (a: number) => a; // 正确myFunc = (a: number, b: number, c: number) => a + b + c; // 报错

如果一个变量要套用另一个函数类型,可以使用 typeof 运算符。

function add(x: number, y: number) {return x + y;
}const myAdd: typeof add = function (x, y) {return x + y;
};

函数类型还可以采用对象的写法。

{(参数列表): 返回值
}
let add: {(x: number, y: number): number;
};add = function (x, y) {return x + y;
};

对象写法非常适用在函数有属性的场合。

function f(x: number) {console.log(x);
}
f.version = "1.0";let foo: {(x: number): void;version: string;
} = f;

函数类型也可以使用 Interface 来声明。

interface myfn {(a: number, b: number): number;
}var add: myfn = (a, b) => a + b;

Function 类型

TypeScript 提供 Function 类型表示函数,任何函数都属于这个类型。

function doSomething(f: Function) {return f(1, 2, 3);
}

Function 类型的值都可以直接执行。

Function 类型的函数可以接受任意数量的参数,每个参数的类型都是 any,返回值的类型也是 any,代表没有任何约束,所以不建议使用这个类型,给出函数详细的类型声明会更好。

箭头函数

const repeat = (str: string, times: number): string => str.repeat(times);
function greet(fn: (a: string) => void): void {fn("world");
}

fn 的类型是(a: string) => void

type Person = { name: string };const people = ["alice", "bob", "jan"].map((name): Person => ({ name }));

people 的类型是 Person[]

可选参数

如果函数的某个参数可以省略,则在参数名后面加问号表示。

参数名带有问号,表示该参数的类型实际上是原始类型|undefined,它有可能为 undefined

function f(x?: number) {return x;
}f(undefined); // 正确
f(); // 正确
f(10); // 正确

函数的可选参数只能在参数列表的尾部,跟在必选参数的后面。

函数体内部用到可选参数时,需要判断该参数是否为 undefined

let myFunc: (a: number, b?: number) => number;myFunc = function (x, y) {if (y === undefined) {return x;}return x + y;
};

参数默认值

TypeScript 函数的参数默认值写法,与 JavaScript 一致。

设置了默认值的参数,就是可选的。如果不传入该参数,它就会等于默认值。

可选参数与默认值不能同时使用。

设有默认值的参数,如果传入 undefined,也会触发默认值。

function f(x = 456) {return x;
}f(undefined); // 456

具有默认值的参数如果不位于参数列表的末尾,调用时不能省略,如果要触发默认值,必须显式传入 undefined

function add(x: number = 0, y: number) {return x + y;
}add(1); // 报错
add(undefined, 1); // 正确

参数解构

函数参数如果存在变量解构,类型写法如下:

function f([x, y]: [number, number]) {// ...
}function sum({ a, b, c }: { a: number; b: number; c: number }) {console.log(a + b + c);
}

参数解构可以结合类型别名(type)一起使用,代码会看起来简洁一些。

type ABC = { a: number; b: number; c: number };function sum({ a, b, c }: ABC) {console.log(a + b + c);
}

rest 参数

rest 参数表示函数剩余的所有参数,它可以是数组(剩余参数类型相同),也可能是元组(剩余参数类型不同)。

// rest 参数为数组
function joinNumbers(...nums: number[]) {// ...
}// rest 参数为元组
function f(...args: [boolean, number]) {// ...
}

如果元组里面的参数是可选的,则要使用可选参数。

function f(...args: [boolean, string?]) {}

rest 参数甚至可以嵌套。

function f(...args: [boolean, ...number[]]) {// ...
}
f(true, 12, 45, 67);

rest 参数可以与变量解构结合使用。

function repeat(...[str, times]: [string, number]): string {return str.repeat(times);
}// 等同于
function repeat(str: string, times: number): string {return str.repeat(times);
}

readonly 只读参数

如果函数内部不能修改某个参数,可以在函数定义时,在参数类型前面加上 readonly 关键字,表示这是只读参数。

function arraySum(arr: readonly number[]) {// ...arr[0] = 0; // 报错
}

::: tip
readonly 关键字目前只允许用在数组和元组类型的参数前面,如果用在其他类型的参数前面,就会报错。
:::

void 类型

void 类型表示函数没有返回值。如果返回其他值,就会报错。

void 类型允许返回 undefinednull

::: tip
如果变量、对象方法、函数参数是一个返回值为 void 类型的函数,那么并不代表不能赋值为有返回值的函数。恰恰相反,该变量、对象方法和函数参数可以接受返回任意值的函数,这时并不会报错。但是如果后面使用了这个函数的返回值,就违反了约定,则会报错。
:::

type voidFunc = () => void;const f: voidFunc = () => {return 123;
};f() * 2; // 报错

函数的运行结果如果是抛出错误,也允许将返回值写成 void

function throwErr(): void {throw new Error("something wrong");
}

never 类型

never 类型表示肯定不会出现的值。它用在函数的返回值,就表示某个函数肯定不会返回值,即函数不会正常执行结束。它主要有以下两种情况。

(1)抛出错误的函数。

function fail(msg: string): never {throw new Error(msg);
}

:::tip
只有抛出错误,才是 never 类型。如果显式用 return 语句返回一个 Error 对象,返回值就不是 never 类型。
:::

由于抛出错误的情况属于 never 类型或 void 类型,所以无法从返回值类型中获知,抛出的是哪一种错误。

(2)无限执行的函数。

const sing = function (): never {while (true) {console.log("sing");}
};

:::tip
never 类型不同于 void 类型。前者表示函数没有执行结束,不可能有返回值;后者表示函数正常执行结束,但是不返回值,或者说返回 undefined
:::

如果一个函数抛出了异常或者陷入了死循环,那么该函数无法正常返回一个值,因此该函数的返回值类型就是 never。如果程序中调用了一个返回值类型为 never 的函数,那么就意味着程序会在该函数的调用位置终止,永远不会继续执行后续的代码。

function neverReturns(): never {throw new Error();
}function f(x: string | undefined) {if (x === undefined) {neverReturns();}x; // 推断为 string
}

一个函数如果某些条件下有正常返回值,另一些条件下抛出错误,这时它的返回值类型可以省略 never

function sometimesThrow(): number {if (Math.random() > 0.5) {return 100;}throw new Error("Something went wrong");
}const result = sometimesThrow();

因为 never 是 TypeScript 的唯一一个底层类型,所有其他类型都包括了 never

局部类型

函数内部允许声明其他类型,该类型只在函数内部有效,称为局部类型。

function hello(txt: string) {type message = string;let newTxt: message = "hello " + txt;return newTxt;
}const newTxt: message = hello("world"); // 报错

高阶函数

一个函数的返回值还是一个函数,那么前一个函数就称为高阶函数(higher-order function)。

(someValue: number) => (multiplier: number) => someValue * multiplier;

函数重载

有些函数可以接受不同类型或不同个数的参数,并且根据参数的不同,会有不同的函数行为。这种根据参数类型不同,执行不同逻辑的行为,称为函数重载(function overload)。

TypeScript 对于“函数重载”的类型声明方法是,逐一定义每一种情况的类型。

// 列举重载的各种情况
function reverse(str: string): string;
function reverse(arr: any[]): any[];
// 函数本身的类型声明
function reverse(stringOrArray: string | any[]): string | any[] {if (typeof stringOrArray === "string") {return stringOrArray.split("").reverse().join("");} else {return stringOrArray.slice().reverse();}
}

函数本身的类型声明,它必须与前面已有的重载声明兼容。

:::tip
重载的各个类型描述与函数的具体实现之间,不能有其他代码,否则报错。
:::

函数重载的每个类型声明之间,以及类型声明与函数实现的类型之间,不能有冲突。

重载声明的排序很重要,因为 TypeScript 是按照顺序进行检查的,一旦发现符合某个类型声明,就不再往下检查了,所以类型最宽的声明应该放在最后面,防止覆盖其他类型声明。

对象的方法也可以使用重载。

class StringBuilder {#data = "";add(num: number): this;add(bool: boolean): this;add(str: string): this;add(value: any): this {this.#data += String(value);return this;}toString() {return this.#data;}
}

函数重载也可以用来精确描述函数参数与返回值之间的对应关系。

function createElement(tag: "a"): HTMLAnchorElement;
function createElement(tag: "canvas"): HTMLCanvasElement;
function createElement(tag: "table"): HTMLTableElement;
function createElement(tag: string): HTMLElement {// ...
}

也可以用对象表示为

type CreateElement = {(tag: "a"): HTMLAnchorElement;(tag: "canvas"): HTMLCanvasElement;(tag: "table"): HTMLTableElement;(tag: string): HTMLElement;
};

一般来说,如果可以的话,应该优先使用联合类型替代函数重载,除非多个参数之间、或者某个参数与返回值之间,存在对应关系。

// 写法一
function len(s: string): number;
function len(arr: any[]): number;
function len(x: any): number {return x.length;
}// 写法二
function len(x: any[] | string): number {return x.length;
}

构造函数

构造函数的最大特点,就是必须使用 new 命令调用。

构造函数的类型写法,就是在参数列表前面加上 new 命令。

class Animal {numLegs: number = 4;
}type AnimalConstructor = new () => Animal;function create(c: AnimalConstructor): Animal {return new c();
}const a = create(Animal);

构造函数还有另一种类型写法,就是采用对象形式。

type F = {new (s: string): object;
};

某些函数既是构造函数,又可以当作普通函数使用。类型声明可以写成下面这样。

type F = {new (s: string): object;(n?: number): number;
};

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

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

相关文章

【R语言】主成分分析与因子分析

一、主成分分析 主成分分析(Principal Component Analysis, PCA)是一种常用的无监督数据降维技术,广泛应用于统计学、数据科学和机器学习等领域。它通过正交化线性变换将(高维)原始数据投影到一个新的坐标系&#xff…

pycharm画图程序如何一步一步的调试

1.设置合适的 Matplotlib 后端 在 PyCharm 中,有时需要手动指定 Matplotlib 后端。你可以尝试在脚本的最开始加入以下代码,强制使用 TkAgg 后端,这样可以保证图形更新的实时性: import matplotlib matplotlib.use(TkAgg) # 指定…

基于Java+Swing+Mysql实现旅游管理信息系统

基于JavaSwingMysql实现旅游管理信息系统 一、系统介绍二、功能展示1.登陆2.注册3.旅游信息查询4.查看游行团信息5.报名6、报名信息管理 三、数据库四、其它1.其他系统实现五.获取源码 一、系统介绍 用户:登陆、注册、旅游信息查询、查看游行团信息、报名 管理员&a…

Linux配置端口映射——其他机器可以访问

一般使用虚拟机都是NAT网络模式,但是这种模式的问题是:其他机器不能访问虚拟机 想让其他机器访问这个电脑上的虚拟机,需要做端口映射。 之后就可以使用finalshell连接 注意:如果要连接其他人的虚拟机,需要对方先关闭自…

快速部署deepseek

一、安装ollama 访问https://ollama.com/download 下载并安装对应系统的ollama。 Ollama 是一个开源工具,旨在帮助用户在本地机器上轻松运行和管理大型语言模型(LLM)。它提供了一个简单易用的命令行界面,可以下载、安装和运行各…

用Deepseek查询快证API-物流查询-实名认证-企业实名认证

快证API可能是一个提供多种验证和查询服务的平台,包括但不限于企业实名认证、短链接生成、手机号归属地查询、IP地址查询等。以下是根据搜索结果整理的关于快证API的相关信息: ‌企业实名认证API‌: 功能:通过与企业相关数据库进行…

基于指纹识别技术的考勤打卡设计与实现(论文+源码)

1 系统总体设计 本次基于指纹识别技术的考勤打卡系统的整体框图如图2.1所示,主控制模块选用单片机STC89C52,同时还包括AT24C02存储电路,指纹模块,LCD12864液晶,继电器,矩阵键盘等硬件电路。其中指纹模块和…

【云安全】云原生-K8S(四)安全问题分析

Kubernetes(K8S)因其强大的容器编排能力成为了云计算和微服务架构的首选,但同时也带来了复杂的安全挑战。本文将概述K8S的主要安全问题,帮助安全工程师理解潜在威胁,并采取相应的防护措施。 K8S 攻击面概览 下面两张…

基于JAVA毕业生信息招聘信息平台设计与实现

以往的毕业生信息招聘信息管理事务处理主要使用的是传统的人工管理方式,这种管理方式存在着管理效率低、操作流程繁琐、保密性差等缺点,长期的人工管理模式会产生大量的文本文件与文本数据,这对事务的查询、更新以及维护带来不少困难。随着互…

ES6模块化和CommonJs模块化区别

ES6模块化和CommonJs模块化区别 在JavaScript中,模块化是将代码拆分成独立的块,每个块可以独立封装和管理。ES6模块化和CommonJS是两种常见的模块化规范,它们在语法、加载方式和运行时特性上有显著差异。 语法差异 CommonJS模块使用requir…

DeepSeek自动化写作软件

DeepSeek写作软件的三大核心功能 对于内容创作者来说,写作不仅是表达思想的过程,更是一项需要投入大量时间和精力的任务。面对日益增长的内容需求,写作效率低下、内容质量不高等问题,常常让创作者感到焦虑。而 DeepSeek 写作软件…

深入解析 Flutter Bloc:从原理到实战

深入解析 Flutter Bloc:从原理到实战 Bloc(Business Logic Component)是 Flutter 中一个强大的状态管理工具,基于事件驱动的架构设计,适合管理复杂的业务逻辑和状态。Bloc 的核心理念是将业务逻辑与 UI 分离&#xff…

使用右侧值现象来处理一个word导入登记表的需求

需求也简单,导word文件用户登记表,有各部门的十几个版本(为什么这么多?不知道)。这里说下谈下我的一些代码做法: 需求分析: 如果能解决java字段和各项填的值怎么配对的问题,那么就…

Day48(补)【AI思考】-设计模式三大类型统一区分与记忆指南

文章目录 设计模式三大类型统一区分与记忆指南**一、创建型模式(对象如何生?)****二、结构型模式(对象如何组?)****三、行为型模式(对象如何动?)****1. 行为型类模式&…

Rook-ceph(1.92最新版)

安装前准备 #确认安装lvm2 yum install lvm2 -y #启用rbd模块 modprobe rbd cat > /etc/rc.sysinit << EOF #!/bin/bash for file in /etc/sysconfig/modules/*.modules do[ -x \$file ] && \$file done EOF cat > /etc/sysconfig/modules/rbd.modules &l…

Transformer技术报告:架构与原理

【深度学习】Transformer 技术报告&#xff1a;架构与原理 一、引言二、Transformer 的基本架构2.1 总体架构2.2 编码器&#xff08;Encoder&#xff09;2.3 解码器&#xff08;Decoder&#xff09;2.4 输入嵌入与位置编码 三、Transformer 的关键特性四、应用场景五、总结 一、…

电子制造企业数字化转型实战:基于Odoo构建MES平台的深度解决方案

作者背景 拥有8年乙方项目经理经验、8年甲方信息化管理经验&#xff0c;主导过12个Odoo制造业项目落地&#xff0c;服务客户涵盖消费电子、汽车电子、工业设备等领域。本文基于华东某电子企业&#xff08;以下简称"A公司"&#xff09;的实战案例&#xff0c;解析行业…

【实战】用飞书多维表格+AI DeepSeeker做股票量价分析

用2万元起步资金&#xff0c;进行A股实战模拟。&#xff08;量化分析无法知晓 消息面的事宜&#xff0c;是一个不足&#xff0c;但是可以代替 哪些一般水平的 股票分析师&#xff09; https://zk4wn8rhv2.feishu.cn/base/OABmbEBa4a4zgOsw5JlcrfIPnzh?tabletblMK2bDhPW5Am9b&a…

计算四个锚点TOA定位中GDOP的详细步骤和MATLAB例程

该MATLAB代码演示了在三维空间中,使用四个锚点的TOA(到达时间)定位技术计算几何精度衰减因子(GDOP)的过程。如需帮助,或有导航、定位滤波相关的代码定制需求,请联系作者 文章目录 DOP计算原理MATLAB例程运行结果示例关键点说明扩展方向另有文章: 多锚点Wi-Fi定位和基站…

Vue 记录用户进入页面的时间、离开页面的时间并计算时长

在 Vue 项目中&#xff0c;要记录用户进入页面的时间、离开页面的时间&#xff0c;并在用户离开时计算时长并调用后端接口&#xff0c;可以借助 Vue 的生命周期钩子和浏览器的一些事件来实现。以下是具体的实现步骤和示例代码&#xff1a; 实现思路 记录进入时间&#xff1a;…