当前位置: 首页 > news >正文

Typescript入门-函数讲解

    • Function 类型
    • 箭头函数
    • 可选参数
    • 参数默认值
    • 参数解构
    • rest参数
    • readonly 只读参数
    • void 类型
    • 构造函数

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

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

上面示例中,函数 hello() 在声明时,需要给出参数 txt 的类型(string),以及返回值的类型(void),后者写在参数列表的圆括号后面。void类型表示没有返回值,详见后文。

如果不指定参数类型(比如上例不写txt的类型),TypeScript 就会推断参数类型,如果缺乏足够信息,就会推断该参数的类型为any。

返回值的类型通常可以不写,因为 TypeScript 自己会推断出来。

如果不指定参数类型(比如上例不写txt的类型),TypeScript 就会推断参数类型,如果缺乏足够信息,就会推断该参数的类型为 any

返回值的类型通常可以不写,因为 TypeScript 自己会推断出来。

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

上面示例中,由于没有return语句,TypeScript 会推断出函数hello()没有返回值。

不过,有时候出于文档目的,或者为了防止不小心改掉返回值,还是会写返回值的类型。

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

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

上面示例中,变量hello被赋值为一个函数,它的类型有两种写法。写法一是通过等号右边的函数类型,推断出变量hello的类型;写法二则是使用箭头函数的形式,为变量hello指定类型,参数的类型写在箭头左侧,返回值的类型写在箭头右侧。

写法二有两个地方需要注意。

首先,函数的参数要放在圆括号里面,不放会报错。

其次,类型里面的参数名(本例是txt)是必须的。有的语言的函数类型可以不写参数名(比如 C 语言),但是 TypeScript 不行。如果写成(string) => void,TypeScript 会理解成函数有一个名叫 string 的参数,并且这个string参数的类型是any。

type MyFunc = (string, number) => number;
// (string: any, number: any) => number

上面示例中,函数类型没写参数名,导致 TypeScript 认为参数类型都是any。

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

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

上面示例中,函数类型里面的参数名为x,实际的函数定义里面,参数名为y,两者并不相同。

如果函数的类型定义很冗长,或者多个函数使用同一种类型,写法二用起来就很麻烦。因此,往往用type命令为函数类型定义一个别名,便于指定给其他变量。

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

上面示例中,type命令为函数类型定义了一个别名MyFunc,后面使用就很方便,变量可以指定为这个类型。

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

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

上面示例中,变量myFunc的类型只能接受两个参数,如果被赋值为只有一个参数的函数,并不报错。但是,被赋值为有三个参数的函数,就会报错。

这是因为 JavaScript 函数在声明时往往有多余的参数,实际使用时可以只传入一部分参数。比如,数组的 forEach() 方法的参数是一个函数,该函数默认有三个参数(item, index, array) => void,实际上往往只使用第一个参数(item) => void。因此,TypeScript 允许函数传入的参数不足。

let x = (a: number) => 0;
let y = (b: number, s: string) => 0;y = x; // 正确
x = y; // 报错

上面示例中,函数x只有一个参数,函数y有两个参数,x可以赋值给y,反过来就不行。

如果一个变量要套用另一个函数类型,有一个小技巧,就是使用 typeof 运算符。

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

上面示例中,函数myAdd()的类型与函数add()是一样的,那么就可以定义成typeof add。因为函数名add本身不是类型,而是一个值,所以要用 typeof 运算符返回它的类型。

这是一个很有用的技巧,任何需要类型的地方,都可以使用 typeof 运算符从一个值获取类型。

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

let add: {(x: number, y: number): number;
};add = function (x, y) {return x + y;
};

上面示例中,变量add的类型就写成了一个对象。

函数类型的对象写法如下。

{(参数列表): 返回值
}

注意,这种写法的函数参数与返回值之间,间隔符是冒号:,而不是正常写法的箭头=>,因为这里采用的是对象类型的写法,对象的属性名与属性值之间使用的是冒号。

这种写法平时很少用,但是非常合适用在一个场合:函数本身存在属性

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

上面示例中,函数f()本身还有一个属性version。这时,f完全就是一个对象,类型就要使用如下对象的写法。

let foo: {(x: number): void;version: string;
} = f;

函数类型也可以使用 Interface 来声明,这种写法就是对象写法的翻版

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

上面示例中,interface 命令定义了接口myfn,这个接口的类型就是一个用对象表示的函数。

Function 类型

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

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

上面示例中,参数f的类型就是 Function,代表这是一个函数。

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

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

箭头函数

箭头函数是普通函数的一种简化写法,它的类型写法与普通函数类似。

const repeat = (str: string, times: number): string => str.repeat(times);

上面示例中,变量repeat被赋值为一个箭头函数,类型声明写在箭头函数的定义里面。其中,参数的类型写在参数名后面,返回值类型写在参数列表的圆括号后面。

注意,类型写在箭头函数的定义里面,与使用箭头函数表示函数类型,写法有所不同。

function greet(fn: (a: string) => void): void {fn("world");
}

上面示例中,函数greet()的参数fn是一个函数,类型就用箭头函数表示。这时,fn的返回值类型要写在箭头右侧,而不是写在参数列表的圆括号后面

下面再看一个例子。

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

上面示例中,Person是一个类型别名,代表一个对象,该对象有属性name。变量people是数组的 map() 方法的返回值。

map() 方法的参数是一个箭头函数 (name):Person => ({name}),该箭头函数的参数name的类型省略了,因为可以从 map() 的类型定义推断出来,箭头函数的返回值类型为 Person。相应地,变量people的类型是Person[]。

至于箭头后面的 ({name}),表示返回一个对象,该对象有一个属性name,它的属性值为变量name的值。这里的圆括号是必须的,否则 (name):Person => {name} 的大括号表示函数体,即函数体内有一行语句name,同时由于没有return语句,这个函数不会返回任何值。

注意,下面两种写法都是不对的。

// 错误
(name: Person) => ({ name });// 错误
name: (Person) => ({ name });

上面的两种写法在本例中都是错的。第一种写法表示,箭头函数的参数name的类型是Person,同时没写函数返回值的类型,让 TypeScript 自己去推断。第二种写法中,函数参数缺少圆括号。

可选参数

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

function f(x?: number) {// ...
}f(); // OK
f(10); // OK

上面示例中,参数x后面有问号,表示该参数可以省略。

参数名带有问号,表示该参数的类型实际上是原始类型|undefined,即它有可能为undefined。比如,上例的x虽然类型声明为number,但是实际上是number|undefined

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

上面示例中,参数x是可选的,等同于说x可以赋值为undefined。

但是,反过来就不成立,类型显式设为 undefined 的参数,就不能省略。

function f(x: number | undefined) {return x;
}f(); // 报错

上面示例中,参数x的类型是 number|undefined,表示要么传入一个数值,要么传入undefined,如果省略这个参数,就会报错。

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

let myFunc: (a?: number, b: number) => number; // 报错

上面示例中,可选参数在必选参数前面,就报错了。

如果前部参数有可能为空,这时只能显式注明该参数类型可能为undefined。

let myFunc: (a: number | undefined, b: number) => number;

上面示例中,参数a有可能为空,就只能显式注明类型包括undefined,传参时也要显式传入undefined。

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

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

上面示例中,由于函数的第二个参数为可选参数,所以函数体内部需要判断一下,该参数是否为空。

参数默认值

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

function createPoint(x: number = 0, y: number = 0): [number, number] {return [x, y];
}createPoint(); // [0, 0]

上面示例中,参数x和y的默认值都是0,调用createPoint()时,这两个参数都是可以省略的。这里其实可以省略x和y的类型声明,因为可以从默认值推断出来。

function createPoint(x = 0, y = 0) {return [x, y];
}

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

// 报错
function f(x?: number = 0) {// ...
}

上面示例中,x是可选参数,还设置了默认值,结果就报错了。

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

function f(x = 456) {return x;
}f2(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 multiply(n: number, ...m: number[]) {return m.map((x) => n * x);
}

上面示例中,参数m就是 rest 类型,它的类型是一个数组。

rest 参数甚至可以嵌套。

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

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; // 报错
}

上面示例中,参数arr的类型是 readonly number[],表示为只读参数。如果函数体内部修改这个数组,就会报错。

void 类型

void 类型表示函数没有返回值。

function f(): void {console.log("hello");
}

上面示例中,函数f没有返回值,类型就要写成 void

如果返回其他值,就会报错。

function f(): void {return 123; // 报错
}

上面示例中,函数f()的返回值类型是 void,但是实际返回了一个数值,编译时就报错了。

void 类型允许返回undefined或null。

function f(): void {return undefined; // 正确
}function f(): void {return null; // 正确
}

如果打开了 strictNullChecks 编译选项,那么 void 类型只允许返回undefined。如果返回null,就会报错。这是因为 JavaScript 规定,如果函数没有返回值,就等同于返回undefined。

// 打开编译选项 strictNullChecksfunction f(): void {return undefined; // 正确
}function f(): void {return null; // 报错
}

需要特别注意的是,如果变量、函数参数的类型是 void 类型的函数,那么并不代表不能赋值为有返回值的函数。恰恰相反,该变量、函数参数可以接受返回任意值的函数,这时并不会报错。

变量是 void 类型的函数

type voidFunc = () => void;const f: voidFunc = () => {return 123;
};

上面示例中,变量f的类型是 voidFunc,是一个没有返回值的函数类型。但是实际上,f的值是一个有返回值的函数(返回123),编译时不会报错。

函数参数是 void 类型的函数

function runCallback(callback: () => void) {return callback();
}

使用示例

f();      // 输出 123
runCallback(() => {return 'Callback executed!'
});                // 输出 Callback executed!

这是因为,这时 TypeScript 认为,这里的 void 类型只是表示该函数的返回值没有利用价值,或者说不应该使用该函数的返回值。只要不用到这里的返回值,就不会报错。

这样设计是有现实意义的。举例来说,数组方法Array.prototype.forEach(fn)的参数fn是一个函数,而且这个函数应该没有返回值,即返回值类型是void。

但是,实际应用中,很多时候传入的函数是有返回值,但是它的返回值不重要,或者不产生作用。

const src = [1, 2, 3];
const ret = [];src.forEach((el) => ret.push(el));

上面示例中,push() 有返回值,表示新插入的元素在数组里面的位置。但是,对于forEach()方法来说,这个返回值是没有作用的,根本用不到,所以 TypeScript 不会报错。

如果后面使用了这个函数的返回值,就违反了约定,则会报错。

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

上面示例中,最后一行报错了,因为根据类型声明,f()没有返回值,但是却用到了它的返回值,因此报错了。

注意,这种情况仅限于变量、函数参数,函数字面量如果声明了返回值是 void 类型,还是不能有返回值。

function f(): void {return true; // 报错
}const f3 = function (): void {return true; // 报错
};

上面示例中,函数字面量声明了返回void类型,这时只要有返回值(除了undefined和null)就会报错。

除了函数,其他变量声明为void类型没有多大意义,因为这时只能赋值为undefined或者null(假定没有打开 strictNullChecks) 。

let foo: void = undefined;// 没有打开 strictNullChecks 的情况下
let bar: void = null;

构造函数

JavaScript 语言使用构造函数,生成对象的实例。

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

const d = new Date();

上面示例中,date()就是一个构造函数,使用 new 命令调用,返回 Date 对象的实例。

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

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

下面详细解释每一部分:

class Animal {numLegs: number = 4;
}
  • 定义了一个名为 Animal 的类。
  • 该类有一个成员变量 numLegs,类型为 number,默认值为 4。
  • 这表示每个 Animal 实例 numLegs 都为4。
type AnimalConstructor = new () => Animal;
  • 定义了一个类型别名 AnimalConstructor
  • new () => Animal 表示:这是一个构造函数类型,接受零个参数,返回一个 Animal 实例。
  • 这样可以用来约束哪些构造函数可以作为参数进行传递。
function create(c: AnimalConstructor): Animal {return new c();
}
  • 定义了一个名为 create 的函数。
  • 参数 c 的类型是 AnimalConstructor,即必须是一个可以用 new 关键字实例化并返回 Animal 的构造函数。
  • 函数体中通过 new c() 创建并返回一个新的 Animal 实例。
const a = create(Animal);
  • 调用 create 函数,传入 Animal 类本身(类在 TypeScript/JavaScript 中本质上就是构造函数)。
  • 返回值赋给变量 a,此时 a 是一个 Animal 实例。

如果 Animal 构造函数需要参数,比如 constructor(numLegs: number) { ... },则 AnimalConstructor 类型也要相应调整为 new (numLegs: number) => Animal

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

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

上面示例中,类型 F 就是一个构造函数。类型写成一个可执行对象的形式,并且在参数列表前面要加上 new 命令。

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

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

上面示例中,F 既可以当作普通函数执行,也可以当作构造函数使用。

http://www.dtcms.com/a/343475.html

相关文章:

  • 创建一个springboot starter页面
  • LG P2617 Dynamic Rankings Solution
  • 1688 商品详情接口数据全解析(1688.item_get)
  • 关于从零开始写一个TEE OS
  • 如何安装 VMware Workstation 17.5.1?超简单步骤(附安装包下载)
  • Building Systems with the ChatGPT API 使用 ChatGPT API 搭建系统(第四章学习笔记及总结)
  • 一文讲清楚:场景、痛点、需求
  • mainMem.useNamedFile = “FALSE“ 的效果
  • UE5多人MOBA+GAS 52、下载源码构建引擎
  • 如何处理项目中棘手的依赖版本冲突问题
  • 软考中级【网络工程师】第6版教材 第3章 局域网 (下)
  • 构造参数注入解决循环依赖问题
  • 射频电路的完整性简略
  • rt-thread使用sfud挂载qspi flash的trace分析
  • Linux ELF二进制文件数字签名工具:原理与设计思路(C/C++代码实现)
  • SQL聚合情景解读
  • 【笔记】Facefusion3.3.2 之 NSFW 检测屏蔽测试
  • 代码随想录算法训练营27天 | ​​56. 合并区间、738.单调递增的数字、968.监控二叉树(提高)
  • 机器学习6
  • 机器学习-聚类算法
  • 告别研发乱局,决胜项目先机——全星APQP系统,为汽车部件制造商量身打造的数字化研发管理引擎
  • GPT5 / 深度研究功能 无法触发
  • 4.Shell脚本修炼手册---变量进阶知识
  • 加速你的故障排查:使用 Elasticsearch 构建家电手册的 RAG 应用
  • 如何实现文档处理全流程自动化?
  • 如何在日常开发中高效使用 Copilot
  • 无人机高科技,翱翔未来新天地
  • 对比学习与先验知识引导的特征提取网络在胶质瘤高风险复发区域预测中的应用|文献速递-深度学习人工智能医疗图像
  • GS-IR:3D 高斯喷溅用于逆向渲染
  • 2025年08月21日Github流行趋势