Skip to content

TypeScript 函数

函数是 TypeScript 中重要的代码组织和复用方式。TypeScript 提供了丰富的函数特性,包括类型注解、函数重载、箭头函数、可选参数、默认参数、剩余参数等。本文将详细介绍 TypeScript 中的函数。

1. 函数定义

函数声明

typescript
function add(a: number, b: number): number {
  return a + b;
}

console.log(add(1, 2)); // 输出:3

函数表达式

typescript
const add = function(a: number, b: number): number {
  return a + b;
};

console.log(add(1, 2)); // 输出:3

箭头函数

typescript
const add = (a: number, b: number): number => {
  return a + b;
};

// 简化版(当函数体只有一条返回语句时)
const add = (a: number, b: number): number => a + b;

console.log(add(1, 2)); // 输出:3

2. 参数类型

TypeScript 允许为函数参数指定类型,这样可以在编译时检查参数类型是否正确。

typescript
function greet(name: string, age: number): string {
  return `Hello, ${name}! You are ${age} years old.`;
}

console.log(greet("John", 30)); // 输出:Hello, John! You are 30 years old.
// console.log(greet("John", "30")); // 错误:类型 'string' 不能赋值给类型 'number'

3. 返回类型

TypeScript 允许为函数指定返回类型,这样可以在编译时检查函数返回值的类型是否正确。

typescript
function add(a: number, b: number): number {
  return a + b;
}

function greet(name: string): string {
  return `Hello, ${name}!`;
}

function log(message: string): void {
  console.log(message);
}

function throwError(message: string): never {
  throw new Error(message);
}

4. 可选参数

TypeScript 允许函数参数是可选的,使用 ? 标记。

typescript
function greet(name: string, age?: number): string {
  if (age) {
    return `Hello, ${name}! You are ${age} years old.`;
  } else {
    return `Hello, ${name}!`;
  }
}

console.log(greet("John")); // 输出:Hello, John!
console.log(greet("John", 30)); // 输出:Hello, John! You are 30 years old.

5. 默认参数

TypeScript 允许为函数参数设置默认值。

typescript
function greet(name: string, age: number = 18): string {
  return `Hello, ${name}! You are ${age} years old.`;
}

console.log(greet("John")); // 输出:Hello, John! You are 18 years old.
console.log(greet("John", 30)); // 输出:Hello, John! You are 30 years old.

6. 剩余参数

TypeScript 允许函数接收任意数量的参数,使用 ... 标记。

typescript
function sum(...numbers: number[]): number {
  return numbers.reduce((acc, num) => acc + num, 0);
}

console.log(sum(1, 2, 3, 4, 5)); // 输出:15
console.log(sum(1, 2)); // 输出:3

7. 函数重载

TypeScript 允许为同一个函数定义多个签名,这称为函数重载。

typescript
// 函数重载签名
function reverse(str: string): string;
function reverse(arr: number[]): number[];

// 函数实现
function reverse(input: string | number[]): string | number[] {
  if (typeof input === "string") {
    return input.split("").reverse().join("");
  } else {
    return input.slice().reverse();
  }
}

console.log(reverse("hello")); // 输出:olleh
console.log(reverse([1, 2, 3, 4, 5])); // 输出:[5, 4, 3, 2, 1]

8. 箭头函数

箭头函数是一种更简洁的函数写法,它没有自己的 thisargumentssupernew.target

基本语法

typescript
// 无参数
const greet = (): string => "Hello!";

// 单个参数
const greet = (name: string): string => `Hello, ${name}!`;

// 多个参数
const add = (a: number, b: number): number => a + b;

// 函数体多行
const add = (a: number, b: number): number => {
  const result = a + b;
  return result;
};

箭头函数与 this

箭头函数没有自己的 this,它会捕获定义时的 this 值。

typescript
class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  // 使用普通函数
  greet1() {
    setTimeout(function() {
      console.log(`Hello, my name is ${this.name}.`); // 错误:this 指向 setTimeout 的调用者
    }, 1000);
  }

  // 使用箭头函数
  greet2() {
    setTimeout(() => {
      console.log(`Hello, my name is ${this.name}.`); // 正确:this 指向 Person 实例
    }, 1000);
  }
}

const person = new Person("John", 30);
person.greet1(); // 输出:Hello, my name is undefined.
person.greet2(); // 输出:Hello, my name is John.

9. 函数类型

TypeScript 允许定义函数类型,用于描述函数的参数类型和返回类型。

函数类型定义

typescript
type AddFunction = (a: number, b: number) => number;

const add: AddFunction = (a, b) => a + b;

console.log(add(1, 2)); // 输出:3

函数类型作为参数

typescript
type AddFunction = (a: number, b: number) => number;

function calculate(a: number, b: number, operation: AddFunction): number {
  return operation(a, b);
}

const add: AddFunction = (a, b) => a + b;
const subtract: AddFunction = (a, b) => a - b;

console.log(calculate(10, 5, add)); // 输出:15
console.log(calculate(10, 5, subtract)); // 输出:5

函数类型作为返回值

typescript
type AddFunction = (a: number, b: number) => number;

function createAddFunction(): AddFunction {
  return (a, b) => a + b;
}

const add = createAddFunction();
console.log(add(1, 2)); // 输出:3

10. 泛型函数

TypeScript 允许定义泛型函数,用于处理不同类型的参数。

typescript
function identity<T>(value: T): T {
  return value;
}

console.log(identity(1)); // 输出:1
console.log(identity("hello")); // 输出:hello
console.log(identity(true)); // 输出:true

function pair<T, U>(first: T, second: U): [T, U] {
  return [first, second];
}

console.log(pair(1, "hello")); // 输出:[1, "hello"]
console.log(pair("hello", true)); // 输出:["hello", true]

11. 递归函数

递归函数是指在函数体内调用自身的函数。

typescript
function factorial(n: number): number {
  if (n <= 1) {
    return 1;
  }
  return n * factorial(n - 1);
}

console.log(factorial(5)); // 输出:120

function fibonacci(n: number): number {
  if (n <= 1) {
    return n;
  }
  return fibonacci(n - 1) + fibonacci(n - 2);
}

console.log(fibonacci(10)); // 输出:55

12. 异步函数

TypeScript 支持异步函数,使用 asyncawait 关键字。

typescript
async function fetchData(): Promise<string> {
  // 模拟网络请求
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("Data fetched successfully!");
    }, 1000);
  });
}

async function processData() {
  console.log("Start fetching data...");
  const data = await fetchData();
  console.log(data);
  console.log("Data processed successfully!");
}

processData();
// 输出:
// Start fetching data...
// Data fetched successfully!
// Data processed successfully!

13. 函数的最佳实践

1. 使用类型注解

为函数参数和返回值添加类型注解,提高代码的可读性和类型安全性。

typescript
// 推荐
function add(a: number, b: number): number {
  return a + b;
}

// 不推荐
function add(a, b) {
  return a + b;
}

2. 优先使用箭头函数

对于简短的函数,优先使用箭头函数,使代码更简洁。

typescript
// 推荐
const add = (a: number, b: number): number => a + b;

// 不推荐
function add(a: number, b: number): number {
  return a + b;
}

3. 使用默认参数替代可选参数

当参数有合理的默认值时,使用默认参数替代可选参数。

typescript
// 推荐
function greet(name: string, age: number = 18): string {
  return `Hello, ${name}! You are ${age} years old.`;
}

// 不推荐
function greet(name: string, age?: number): string {
  const actualAge = age || 18;
  return `Hello, ${name}! You are ${actualAge} years old.`;
}

4. 使用剩余参数处理可变数量的参数

当函数需要处理可变数量的参数时,使用剩余参数。

typescript
// 推荐
function sum(...numbers: number[]): number {
  return numbers.reduce((acc, num) => acc + num, 0);
}

// 不推荐
function sum() {
  let total = 0;
  for (let i = 0; i < arguments.length; i++) {
    total += arguments[i];
  }
  return total;
}

5. 使用函数重载处理不同类型的参数

当函数需要处理不同类型的参数时,使用函数重载。

typescript
// 推荐
function reverse(str: string): string;
function reverse(arr: number[]): number[];
function reverse(input: string | number[]): string | number[] {
  if (typeof input === "string") {
    return input.split("").reverse().join("");
  } else {
    return input.slice().reverse();
  }
}

// 不推荐
function reverse(input: any): any {
  if (typeof input === "string") {
    return input.split("").reverse().join("");
  } else if (Array.isArray(input)) {
    return input.slice().reverse();
  }
}

6. 避免使用 any 类型

尽量避免使用 any 类型,使用更具体的类型或泛型。

typescript
// 推荐
function identity<T>(value: T): T {
  return value;
}

// 不推荐
function identity(value: any): any {
  return value;
}

14. 常见错误

1. 忘记添加类型注解

忘记为函数参数和返回值添加类型注解,导致类型检查失效。

typescript
// 错误
function add(a, b) {
  return a + b;
}

// 正确
function add(a: number, b: number): number {
  return a + b;
}

2. 箭头函数的 this 绑定

在使用箭头函数时,没有注意到它会捕获定义时的 this 值。

typescript
// 错误
class Person {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  greet() {
    setTimeout(() => {
      console.log(`Hello, my name is ${this.name}.`);
    }, 1000);
  }
}

// 正确(与上面相同,因为箭头函数会捕获定义时的 this)
class Person {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  greet() {
    setTimeout(() => {
      console.log(`Hello, my name is ${this.name}.`);
    }, 1000);
  }
}

3. 函数重载的顺序

函数重载的顺序很重要,应该将更具体的重载签名放在前面。

typescript
// 错误
function reverse(arr: number[]): number[];
function reverse(str: string): string;
function reverse(input: string | number[]): string | number[] {
  // 实现
}

// 正确
function reverse(str: string): string;
function reverse(arr: number[]): number[];
function reverse(input: string | number[]): string | number[] {
  // 实现
}

4. 递归函数的类型注解

递归函数需要显式添加返回类型注解,否则 TypeScript 可能无法推断类型。

typescript
// 错误
function factorial(n: number) {
  if (n <= 1) {
    return 1;
  }
  return n * factorial(n - 1);
}

// 正确
function factorial(n: number): number {
  if (n <= 1) {
    return 1;
  }
  return n * factorial(n - 1);
}

总结

TypeScript 提供了丰富的函数特性,包括:

  • 函数定义:函数声明、函数表达式、箭头函数
  • 参数类型:为函数参数指定类型
  • 返回类型:为函数指定返回类型
  • 可选参数:使用 ? 标记可选参数
  • 默认参数:为参数设置默认值
  • 剩余参数:使用 ... 处理可变数量的参数
  • 函数重载:为同一个函数定义多个签名
  • 箭头函数:更简洁的函数写法,没有自己的 this
  • 函数类型:定义函数的类型
  • 泛型函数:处理不同类型的参数
  • 递归函数:在函数体内调用自身
  • 异步函数:使用 asyncawait 处理异步操作

通过合理使用这些函数特性,你可以编写更清晰、更类型安全的 TypeScript 代码。