6. TypeScript 函数
函数是 TypeScript 的核心构建模块之一。本节重点介绍 TypeScript 如何通过添加强类型来增强 JavaScript 函数,从而帮助创建可重用、灵活且类型安全的函数。它确保函数的参数和返回类型保持一致,避免错误的发生。
一、认识函数
TypeScript 函数是用于执行特定任务的可重用代码块。它们支持面向对象编程的原则,如类和多态。函数可以为参数和返回值添加类型注解,从而提升代码的清晰度、安全性和可维护性。
(一) 语法
function functionName(arg: argType) {// 函数体
}
其中:
- functionName:函数的名称
- arg:参数名称
- argType:参数的类型
(二) 场景示例
1. 参数类型注解
TypeScript 中的参数类型注解用于指定每个函数参数的类型,确保函数接收到的参数具有正确的类型。
在这个示例中,我们定义了一个 greet
函数,它接受一个类型为 string
的参数 name
,并输出一条问候消息。
function greet(name: string): void {// 输出问候语console.log(`Hello, ${name}`);
}
greet("Alice");
输出:
Hello, Alice
2. 返回类型注解
我们可以在函数参数列表之后写明函数的返回类型,这称为返回类型注解。
在这个示例中,我们定义了一个 add
函数,它接受两个数字类型的参数,并返回它们的和。
function add(a: number, b: number): number {return a + b;
}
console.log(add(2, 3));
输出:
5
3. 返回 Promise 的函数
在 TypeScript 中,返回 Promise 的函数会将返回类型指定为 Promise<Type>
,表示异步操作会返回指定类型的值。
在这个示例中,我们定义了一个异步函数 greet
,它返回一个包含字符串的 Promise。
async function greet(): Promise<string> {return ("Hello, TypeScript!!");
}
greet().then((result) => {console.log(result);}
)
输出:
Hello, TypeScript!!
4. 匿名函数
匿名函数是在运行时定义的无名函数,并存储在变量中。它接受输入,返回输出,可以通过变量名来调用。
在这个示例中,我们定义了一个匿名函数用于计算数字的平方,并将其赋值给变量 square
。
const square = function(num: number): number {return num * num;
};
console.log(square(4));
输出:
16
(三) 总结
在本文中,我们探讨了多种 TypeScript 函数类型,包括参数类型注解、返回类型注解、返回 Promise 的函数以及匿名函数。这些特性提升了代码的清晰度、安全性、可重用性和可维护性,使得编写和管理 TypeScript 代码更加轻松。
二、剩余参数
TypeScript 中的剩余参数允许函数通过将多个参数收集到一个数组中,来处理任意数量的参数。它们使用 ...
定义,且必须是参数列表中的最后一个参数。
- 允许函数灵活且动态地处理输入。
- 简化了处理多个参数时,无需逐个指定参数的操作。
(一) 语法
function function_name(...rest: type[]) {// rest 的类型是 type 类型的数组。
}
参数说明:
- functionName:函数名称。
- ...rest:剩余参数,将所有额外的参数收集到一个数组中。
- type[]:指定剩余参数数组中元素的类型(例如,
number[]
、string[]
)。
function sum(...numbers: number[]): number {return numbers.reduce((total, num) => total + num, 0);
}console.log(sum(1, 2, 3));
console.log(sum(10, 20));
sum
函数使用剩余参数...numbers
将传入的所有参数收集到一个类型为number[]
的数组中。- 对
numbers
数组调用reduce
方法,将所有元素相加,计算出总和。
输出:
6
30
(二) 场景示例
1. 计算数字的平均值
function average(...numbers: number[]): number {let total = 0;for (let num of numbers) {total += num;}return numbers.length === 0 ? 0 : total / numbers.length;
}console.log("给定数字的平均值是:", average(10, 20, 30, 60));
console.log("给定数字的平均值是:", average(5, 6));
console.log("给定数字的平均值是:", average(4));
average
函数使用剩余参数...numbers
来接受任意数量的数字参数。- 它计算这些数字的总和并返回它们的平均值。
输出:
给定数字的平均值是: 30
给定数字的平均值是: 5.5
给定数字的平均值是: 4
2. 连接字符串
function joinStrings(...strings: string[]): string {return strings.join(', ');
}console.log(joinStrings("rachel", "john", "peter") + " are mathematicians");
console.log(joinStrings("sarah", "joseph") + " are coders");
joinStrings
函数使用剩余参数来接收多个字符串参数。- 它将这些字符串用逗号连接成一个单一的字符串。
输出:
rachel, john, peter are mathematicians
sarah, joseph are coders
3. 错误使用剩余参数
// 错误用法 - 会导致编译错误
function job(...people: string[], jobTitle: string): void {console.log(`${people.join(', ')} are ${jobTitle}`);
}// 取消注释下面这行会引发编译错误
// job("rachel", "john", "peter", "mathematicians");
- 在这个示例中,剩余参数
...people
没有放在参数列表的最后。 - TypeScript 要求剩余参数必须是最后一个参数,否则会导致编译错误。
输出:TypeScript 编译器报错。
main.ts(2,14): error TS1014: A rest parameter must be last in a parameter list.
(三) 剩余参数使用的最佳实践
- 将剩余参数放在最后:始终在参数列表的末尾定义剩余参数,以确保函数行为正确。
- 使用合适的类型:为剩余参数指定正确的数组类型,以保持类型安全和代码清晰。
- 限制为一个剩余参数:一个函数中应仅使用一个剩余参数,以避免复杂性和潜在错误。
- 避免过度使用:应谨慎使用剩余参数,过度使用会导致代码难以理解和维护。
三、函数重载
TypeScript 函数重载允许为一个函数定义多个签名,使其能够处理不同类型或数量的参数。
- 增强类型安全,确保正确处理参数。
- 提高代码的灵活性和可读性。
function greet(person: string): string;
function greet(person: string, age: number): string;
function greet(person: string, age?: number): string {if (age !== undefined) {return `Hello, ${person}! You are ${age} years old.`;}return `Hello, ${person}!`;
}console.log(greet("Alice"));
console.log(greet("Bob", 30));
- 函数
greet
有两个重载:一个带有单个参数person
,另一个带有person
和age
两个参数。 - 函数实现中会检查是否传入了
age
,并返回相应的问候语。
输出:
Hello, Alice!
Hello, Bob! You are 30 years old.
(一) 场景示例
1. 数字相加或字符串连接
function combine(a: number, b: number): number;
function combine(a: string, b: string): string;
function combine(a: any, b: any): any {return a + b;
}console.log(combine(5, 10));
console.log(combine("Hello, ", "World!"));
combine
函数通过重载支持处理数字和字符串,可以执行相加或连接操作。- 函数实现采用单个函数来处理这两种情况。
输出:
15
Hello, World!
2. 通过 ID 或查询条件获取数据
function fetchData(id: number): string;
function fetchData(query: string): string[];
function fetchData(param: any): any {if (typeof param === 'number') {return `ID 为 ${param} 的数据`;} else {return [`查询 "${param}" 的结果`];}
}console.log(fetchData(42));
console.log(fetchData("search term"));
fetchData
函数通过重载支持接收数字类型的 ID 或字符串类型的查询条件。- 当传入 ID 时,返回一个字符串;传入查询条件时,返回一个字符串数组。
输出:
ID 为 42 的数据
[ '查询 "search term" 的结果' ]
3. 计算不同形状的面积
function calculateArea(radius: number): number;
function calculateArea(length: number, width: number): number;
function calculateArea(...args: number[]): number {if (args.length === 1) {return Math.PI * args[0] ** 2;} else {return args[0] * args[1];}
}console.log(calculateArea(5));
console.log(calculateArea(10, 20));
calculateArea
函数通过重载实现:当传入一个参数时计算圆的面积,传入两个参数时计算矩形的面积。- 它使用剩余参数来处理可变数量的参数。
输出:
78.53981633974483
200
(二) 函数重载使用的最佳实践
- 定义清晰且具体的重载:确保每个重载签名准确且明确,提升代码可读性和可维护性。
- 按从最具体到最一般的顺序排列重载:让更具体的签名排在前面,帮助 TypeScript 编译器正确选择重载。
- 实现通用的函数体:函数实现部分应支持所有重载,使用类型保护或条件逻辑恰当处理不同参数类型。
四、箭头函数/Lambda 函数
TypeScript 中的箭头函数(也称为 Lambda 函数)是一种简洁且轻量的函数表达式,使用 =>
语法。
- 提供了更简短的函数定义语法。
- 自动绑定其所在上下文的
this
。 - 常用于回调函数、数组方法以及简单的一行函数。
(一) 语法
(param1, param2, ..., paramN) => expression; // 有多个参数的箭头函数
() => expression; // 无参数的箭头函数
例如:
const greet = (name: string): string => `Hello, ${name}!`;console.log(greet("Alice"));
输出:
Hello, Alice!
(二) 场景示例
1. 箭头函数在类中的使用
class Calculator {add = (a: number, b: number): number => a + b;
}const calc = new Calculator();
console.log(calc.add(5, 3));
- Calculator 类包含一个以箭头函数定义的 add 方法,该方法接收两个数字并返回它们的和。
- 使用箭头函数确保该方法继承了其所在类的 this 上下文。
输出:
8
2. 箭头函数与数组方法
const numbers = [1, 2, 3, 4, 5];
const squared = numbers.map(n => n * n);
console.log(squared);
- 箭头函数被用于 numbers 数组的 map 方法中,对每个数字进行平方操作。
- 这种简洁的语法在对数组元素进行操作时提升了代码的可读性。
输出:
[1, 4, 9, 16, 25]
3. 箭头函数作为回调函数
setTimeout(() => {console.log("这条消息将在1秒后显示。");
}, 1000);
- 箭头函数作为回调传递给 setTimeout,在1秒延迟后打印一条消息。
- 由于箭头函数语法简洁且绑定了词法作用域的 this,非常适合用作内联回调函数。
输出:
这条消息将在1秒后显示。
(三) 箭头函数使用的最佳实践
- 短回调函数使用箭头函数:箭头函数非常适合在
map
、filter
和reduce
等方法中书写简短且简洁的回调函数。 - 避免在类的方法中使用箭头函数:对于需要动态
this
绑定的类方法,应使用普通函数,因为箭头函数会继承定义时的this
上下文。 - 正确返回对象字面量:在单行箭头函数中返回对象字面量时,需用圆括号包裹对象,以避免语法错误。
- 使用箭头函数保持
this
上下文:箭头函数适用于需要在事件处理器或异步代码中保持this
上下文的场景。
五、匿名函数类型
在 TypeScript 中,匿名函数类型定义了一个没有具体名称的函数,同时指定参数和返回类型。这样可以实现灵活且可复用的函数定义,支持将函数赋值给变量,并对参数和返回值进行类型注解。
(一) 语法
let functionName: (param1Type, param2Type, ...) => returnType = function (param1, param2, ...) {// 这里是函数的实现};
参数说明
- functionName:定义匿名函数类型的变量名。
- param1Type, param2Type, ...:函数参数的数据类型,需替换为函数实际期望的类型。
- returnType:函数返回值的类型。
- function(param1, param2, ...):赋值给
functionName
的匿名函数本体。 - param1, param2, ...:匿名函数内部使用的参数名,应与类型注解中的参数名对应。
(二) 场景示例
1. 简单问候函数
在这个例子中,我们声明了变量 greet,其类型为匿名函数类型,接收一个字符串参数(name)并返回一个字符串。然后我们赋值了一个生成问候语的函数,调用它并传入 "TypeScript",最后将结果打印出来。
let greet: (name: string) => string = function (name: string): string {return `Hello, ${name}!`;
};console.log(greet("TypeScript"));
输出:
Hello, TypeScript
2. 数学运算函数
在这个例子中,变量 calculate 被定义为一个函数,接收数字类型的参数 x、y 和字符串类型的参数 operation,返回一个数字。匿名函数根据 operation 参数执行相应的数学运算。
let calculate: (x: number, y: number, operation: string) => number =function (x: number, y: number, operation: string): number {switch (operation) {case "add":return x + y;case "subtract":return x - y;case "multiply":return x * y;case "divide":return x / y;default:throw new Error("无效的操作");}};console.log(calculate(5, 3, "add")); // 计算5和3的加法结果
console.log(calculate(10, 2, "divide")); // 计算10除以2的结果
输出:
8
5