6. TypeScript 函数

函数是 TypeScript 的核心构建模块之一。本节重点介绍 TypeScript 如何通过添加强类型来增强 JavaScript 函数,从而帮助创建可重用、灵活且类型安全的函数。它确保函数的参数和返回类型保持一致,避免错误的发生。

一、认识函数

TypeScript 函数是用于执行特定任务的可重用代码块。它们支持面向对象编程的原则,如类和多态。函数可以为参数和返回值添加类型注解,从而提升代码的清晰度、安全性和可维护性。

(一) 语法

TypeScript 复制代码
function functionName(arg: argType) {
  // 函数体
}

其中:

  • functionName:函数的名称
  • arg:参数名称
  • argType:参数的类型

(二) 场景示例

1. 参数类型注解

TypeScript 中的参数类型注解用于指定每个函数参数的类型,确保函数接收到的参数具有正确的类型。

在这个示例中,我们定义了一个 greet 函数,它接受一个类型为 string 的参数 name,并输出一条问候消息。

TypeScript 复制代码
function greet(name: string): void {
  // 输出问候语
  console.log(`Hello, ${name}`);
}
greet("Alice");

输出:

复制代码
Hello, Alice

2. 返回类型注解

我们可以在函数参数列表之后写明函数的返回类型,这称为返回类型注解。

在这个示例中,我们定义了一个 add 函数,它接受两个数字类型的参数,并返回它们的和。

TypeScript 复制代码
function add(a: number, b: number): number {
  return a + b;
}
console.log(add(2, 3));

输出:

复制代码
5

3. 返回 Promise 的函数

在 TypeScript 中,返回 Promise 的函数会将返回类型指定为 Promise<Type>,表示异步操作会返回指定类型的值。

在这个示例中,我们定义了一个异步函数 greet,它返回一个包含字符串的 Promise。

TypeScript 复制代码
async function greet(): Promise<string> {
  return ("Hello, TypeScript!!");
}
greet().then(
  (result) => {
    console.log(result);
  }
)

输出:

复制代码
Hello, TypeScript!!

4. 匿名函数

匿名函数是在运行时定义的无名函数,并存储在变量中。它接受输入,返回输出,可以通过变量名来调用。

在这个示例中,我们定义了一个匿名函数用于计算数字的平方,并将其赋值给变量 square

TypeScript 复制代码
const square = function(num: number): number {
  return num * num;
};
console.log(square(4));

输出:

复制代码
16

(三) 总结

在本文中,我们探讨了多种 TypeScript 函数类型,包括参数类型注解、返回类型注解、返回 Promise 的函数以及匿名函数。这些特性提升了代码的清晰度、安全性、可重用性和可维护性,使得编写和管理 TypeScript 代码更加轻松。

二、剩余参数

TypeScript 中的剩余参数允许函数通过将多个参数收集到一个数组中,来处理任意数量的参数。它们使用 ... 定义,且必须是参数列表中的最后一个参数。

  • 允许函数灵活且动态地处理输入。
  • 简化了处理多个参数时,无需逐个指定参数的操作。

(一) 语法

TypeScript 复制代码
function function_name(...rest: type[]) {
  // rest 的类型是 type 类型的数组。
}

参数说明:

  • functionName:函数名称。
  • ...rest:剩余参数,将所有额外的参数收集到一个数组中。
  • type[] :指定剩余参数数组中元素的类型(例如,number[]string[])。
TypeScript 复制代码
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. 计算数字的平均值

TypeScript 复制代码
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. 连接字符串

TypeScript 复制代码
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. 错误使用剩余参数

TypeScript 复制代码
// 错误用法 - 会导致编译错误
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 函数重载允许为一个函数定义多个签名,使其能够处理不同类型或数量的参数。

  • 增强类型安全,确保正确处理参数。
  • 提高代码的灵活性和可读性。
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,另一个带有 personage 两个参数。
  • 函数实现中会检查是否传入了 age,并返回相应的问候语。

输出:

复制代码
Hello, Alice!
Hello, Bob! You are 30 years old.

(一) 场景示例

1. 数字相加或字符串连接

TypeScript 复制代码
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 或查询条件获取数据

TypeScript 复制代码
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. 计算不同形状的面积

TypeScript 复制代码
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
  • 常用于回调函数、数组方法以及简单的一行函数。

(一) 语法

TypeScript 复制代码
(param1, param2, ..., paramN) => expression; // 有多个参数的箭头函数
() => expression; // 无参数的箭头函数

例如:

TypeScript 复制代码
const greet = (name: string): string => `Hello, ${name}!`;

console.log(greet("Alice"));

输出:

复制代码
Hello, Alice!

(二) 场景示例

1. 箭头函数在类中的使用

TypeScript 复制代码
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. 箭头函数与数组方法

TypeScript 复制代码
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. 箭头函数作为回调函数

TypeScript 复制代码
setTimeout(() => {
  console.log("这条消息将在1秒后显示。");
}, 1000);
  • 箭头函数作为回调传递给 setTimeout,在1秒延迟后打印一条消息。
  • 由于箭头函数语法简洁且绑定了词法作用域的 this,非常适合用作内联回调函数。

输出:

复制代码
这条消息将在1秒后显示。

(三) 箭头函数使用的最佳实践

  • 短回调函数使用箭头函数 :箭头函数非常适合在 mapfilterreduce 等方法中书写简短且简洁的回调函数。
  • 避免在类的方法中使用箭头函数 :对于需要动态 this 绑定的类方法,应使用普通函数,因为箭头函数会继承定义时的 this 上下文。
  • 正确返回对象字面量:在单行箭头函数中返回对象字面量时,需用圆括号包裹对象,以避免语法错误。
  • 使用箭头函数保持 this****上下文 :箭头函数适用于需要在事件处理器或异步代码中保持 this 上下文的场景。

五、匿名函数类型

在 TypeScript 中,匿名函数类型定义了一个没有具体名称的函数,同时指定参数和返回类型。这样可以实现灵活且可复用的函数定义,支持将函数赋值给变量,并对参数和返回值进行类型注解。

(一) 语法

TypeScript 复制代码
let functionName: (param1Type, param2Type, ...) => returnType = 
  function (param1, param2, ...) {
    // 这里是函数的实现
  };

参数说明

  • functionName:定义匿名函数类型的变量名。
  • param1Type, param2Type, ...:函数参数的数据类型,需替换为函数实际期望的类型。
  • returnType:函数返回值的类型。
  • function(param1, param2, ...) :赋值给 functionName 的匿名函数本体。
  • param1, param2, ...:匿名函数内部使用的参数名,应与类型注解中的参数名对应。

(二) 场景示例

1. 简单问候函数

在这个例子中,我们声明了变量 greet,其类型为匿名函数类型,接收一个字符串参数(name)并返回一个字符串。然后我们赋值了一个生成问候语的函数,调用它并传入 "TypeScript",最后将结果打印出来。

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 参数执行相应的数学运算。

TypeScript 复制代码
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
相关推荐
时光足迹1 分钟前
电子书阅读器之章节拆分
前端·javascript·react.js
无名之逆1 分钟前
大三自学笔记:探索Hyperlane框架的心路历程
java·开发语言·前端·spring boot·后端·rust·编程
WILLF3 分钟前
【JavaScript】原型与原型链
javascript
郭顺发5 分钟前
个人网站大更新,还是有个总站比较好
前端
古夕7 分钟前
Webpack 之 打包后的 bundle 文件内容解析
前端·面试·webpack
朴shu13 分钟前
Avatar-Clipper 轻量级图片裁剪工具
前端·设计模式·开源
古夕14 分钟前
webpack 之 Loader 和 Plugin 接收参数对比
前端·面试·webpack
一只叫煤球的猫18 分钟前
1200行代码的前端组件?这套拆分套路让代码从此优雅
前端·vue.js·性能优化
涵信20 分钟前
第八节 工程化与高级特性-模块与命名空间的选择
前端·javascript·typescript
慢知行26 分钟前
VS Code 插件开发必备:轻量级日志工具的设计与实现
前端·typescript·visual studio code