返回 导航

其他

hangge.com

TypeScript - 数据类型详解5(函数类型:参数、返回值、this、重载)

作者:hangge | 2026-08-04 09:21
        在 TypeScript 中,函数是构成应用程序的重要部分。与 JavaScript 相比,TypeScript 为函数添加了类型系统,让我们可以精确地定义函数的参数类型和返回值类型。本文将详细介绍 TypeScript 中函数类型的各种用法,包括参数类型注解、返回值类型、函数类型表达式、可选参数、默认参数、剩余参数、this 类型以及函数重载等内容。

一、基本介绍

1,什么是函数类型

    在 JavaScript 中,函数是一等公民,可以作为参数传递、作为返回值返回,也可以赋值给变量。TypeScript 在此基础上增加了类型系统,允许我们为函数的参数和返回值指定类型。

2,TypeScript 函数类型的主要特点

  • 类型安全:在编译时检查参数类型和返回值类型,避免运行时错误
  • 智能提示IDE 能够根据类型提供更准确的代码补全
  • 代码可读性:明确的类型定义让函数的输入输出更加清晰
  • 函数重载:支持根据不同的参数类型执行不同的逻辑

二、函数参数和返回值类型

1,参数的类型注解

(1)在声明函数时,可以在每个参数后面添加类型注解,声明函数接收的参数类型。例如,定义一个求和函数:
// 为参数加上类型注解
function sum(num1: number, num2: number) {
  return num1 + num2;
}

// 正确调用
sum(123, 321);  // 返回 444

(2)当调用函数时,如果传入的参数类型或数量不正确,TypeScript 会在编译时报错:
提示TypeScript 的类型检查是在编译时进行的,这可以帮助我们在代码运行之前就发现潜在的类型错误。
// 错误示例:参数数量不足
sum(123); 
// 错误信息:Expected 2 arguments, but got 1.

// 错误示例:参数类型不匹配
sum('123', '321');
// 错误信息:Argument of type 'string' is not assignable to parameter of type 'number'.

2,返回值的类型注解

我们也可以为函数的返回值添加类型注解,在参数列表后面使用冒号指定返回值类型:
提示:在 TypeScript 中,返回值的类型注解通常不需要显式指定。TypeScript 会根据函数内部的 return 语句自动推断返回类型。但为了增加代码的可读性和明确性,许多第三方库会显式指定函数的返回类型。
// 为返回值加上类型注解
function sum(num1: number, num2: number): number {
  return num1 + num2;
}

const result = sum(10, 20);  // result 类型为 number

3,匿名函数的参数类型

(1)在 TypeScript 中,匿名函数和函数声明有所不同。当 TypeScript 能够确定函数在哪里被调用以及如何被调用时,会自动推断出该函数的参数类型。
提示:上下文类型是 TypeScript 类型推断的重要特性之一,它可以减少代码中的类型注解,同时保持类型安全。

(2)下面代码没有明确指定 item 的类型,但根据 forEach 函数和数组的类型,TypeScript 可以推断出 item 应该是字符串类型。这个过程被称为上下文类型Contextual Typing),函数的执行上下文可以帮助我们确定参数和返回值的类型。
const names = ["abc", "cba", "nba"];

// item 的类型会自动推断为 string
names.forEach(item => {
  console.log(item.toUpperCase());  // 可以正常调用字符串方法
});

三、函数类型表达式

1,定义函数类型

(1)在 JavaScript 开发中,函数可以作为参数传递,也可以作为返回值。在使用函数的过程中,我们可以编写函数类型表达式Function Type Expression)来表示函数类型:
// 方式一:不编写函数类型
const add = (a1: number, a2: number) => {
  return a1 + a2;
};

// 方式二:为函数编写函数类型
const add2: (num1: number, num2: number) => number = (a1: number, a2: number) => {
  return a1 + a2;
};

(2)上述语法 (num1: number,num2: number) => number 表示一个函数类型的定义,该函数类型定义了以下特征:
  • 该函数接收两个参数:num1num2,类型均为 number
  • 该函数返回值的类型为 number
注意:在 TypeScript 中,函数类型表达式中的参数名称(如 num1num2)是不可省略的。如果省略参数名称,类型含义会完全改变。例如 (string) => void 表示一个参数名为 string、类型为 any 的函数,而不是接收字符串参数的函数。

2,使用类型别名优化

(1)可以使用类型别名Type Alias)让函数类型更具可读性:
// 使用类型别名定义函数类型
type AddFnType = (num1: number, num2: number) => number;

// 使用类型别名
const add: AddFnType = (a1: number, a2: number) => {
  return a1 + a2;
};

console.log(add(10, 20));  // 输出: 30

(2)当函数作为另一个函数的参数时,也可以使用类型别名:
// 定义函数类型别名
type FooFnType = () => void;

// 函数作为参数时使用类型别名
function bar(fn: FooFnType) {
  fn();
}

// 调用示例
bar(() => {
  console.log('函数被调用了');
});

四、函数参数的特殊形式

1,可选参数

    TypeScript 允许我们将某些参数定义为可选的,使用 ? 标记。下面代码中,y 是可选参数,其类型实际上是 number | undefined。如果没有向 y 传递数据,那么 y 的值就是 undefined
注意:在函数参数列表中,可选参数必须放在必选参数的后面。如果把可选参数放在必选参数前面,会导致编译错误。
// 参数 y 是可选参数
function foo(x: number, y?: number) {
  console.log(x, y);
}

foo(20, 30);   // 输出: 20 30
foo(20);       // 输出: 20 undefined

2,默认参数

    从 ES6 开始,JavaScriptTypeScript 都支持为参数设置默认值。下面样例中参数 y 有一个默认值 20。关于默认参数的类型:
  • 如果向 y 传入了值,则使用传入的值
  • 如果向 y 传入 undefined 或没有传递值,则使用默认值
  • y 的类型实际上是 number | undefined 的联合类型
提示:与可选参数不同,带默认值的参数不需要放在必选参数的后面。如果带默认值的参数出现在必选参数前面,用户必须显式传入 undefined 来获得默认值。
// 参数 y 设默认值为 20
function foo(x: number, y: number = 20) {
  console.log(x, y);
}

foo(30);         // 输出: 30 20(使用默认值)
foo(30, 50);     // 输出: 30 50(使用传入的值)
foo(30, undefined);  // 输出: 30 20(传入 undefined 使用默认值)


3,剩余参数

(1)从 ES6 开始,JavaScript 支持剩余参数语法,允许我们将不定数量的参数收集到一个数组中。
提示:剩余参数会被当做个数不限的可选参数,可以一个都没有,也可以有任意多个。编译器会创建参数数组,名字是省略号(...)后面给定的名称。

(2)下面样例中,...nums 就是剩余参数。当调用 sum 函数时,除了第一个参数 initialNum,其他参数都会传递给 nums 数组。
// ...nums 为剩余参数
function sum(initialNum: number, ...nums: number[]): number {
  let total: number = initialNum;
  
  for (const num of nums) {
    total += num;
  }
  
  return total;
}

console.log(sum(20, 30));        // 输出: 50
console.log(sum(20, 30, 40));    // 输出: 90
console.log(sum(20, 30, 40, 50)); // 输出: 140

五、this 的类型

1,this 的默认推导

(1)在 JavaScript 中,this 是一个比较难理解的概念。简单来说,this 表示当前函数的执行上下文,但在不同的情况下,它所绑定的值是不同的。在 TypeScript 中,this 类型会被分两种情况来处理:
  • TypeScript 可以默认推断出 this 的类型
  • 如果需要,可以手动编写 this 的类型

(3)下面演示 TypeScript 自动推断 this 类型的情况。对于 eating 函数,TypeScript 会默认推断出其外部对象 info 作为 this 类型。
// this 可以被 TypeScript 推导为 info 对象
const info = {
  name: "hangge",
  eating() {
    console.log(this.name + " eating");
  }
};

info.eating();  // 输出: hangge eating


2,显式指定 this 类型

(1)在某些情况下,TypeScript 默认无法推断出 this 的类型。如果没有为 this 编写类型,代码将在编译时报错。
  • 下面代码在打印 this.name 处会报错,提示 this 没有编写类型注解。因为 TypeScript 默认推导不出 this 的类型,所以编译时会报错。
function eating(message: string) {
  // this 报错:'this' implicitly has type 'any' because 
  // it does not have a type annotation.
  console.log(this.name + " eating", message);
}

const info = {
  name: "hangge",
  eating: eating  // 赋值一个 eating 函数
};

// 隐式绑定 this
info.eating("哈哈哈");

// 使用 call 函数显式绑定 this
eating.call({ name: "kobe" }, "呵呵呵");

(2)这时可以为 this 编写类型。注意:this 参数是一个假参数,它出现在参数列表的最前面。
提示this 参数是一个特殊的参数,只用于类型检查,在实际编译后的 JavaScript 代码中会被移除。
// 定义 this 的类型
type ThisType = { name: string };

// 为 this 添加类型注解
function eating(this: ThisType, message: string) {
  console.log(this.name + " eating", message);
}

const info = {
  name: "hangge",
  eating: eating
};

// 正常调用
info.eating("哈哈哈");  // 输出: hangge eating 哈哈哈

六、函数重载

1,什么是函数重载

(1)在编程中,函数重载Function Overloading)是指函数名称相同但参数的数量或类型不同。在 JavaScript 中,我们可以编写非常灵活的函数:
// JavaScript 中的灵活函数
function add(a1, a2) {
  return a1 + a2;
}

// 可以传入各种类型
add(10, 20);           // 数字相加
add('Hello', 'World'); // 字符串拼接

(2)但这种方式非常容易出错,例如传入两个对象可能会导致意外结果。在 TypeScript 中,如果编写一个 add 函数,希望对字符串和数字类型进行相加,有以下两种实现方案:
  • 使用联合类型实现函数重载
  • 使用重载签名实现函数重载

2,使用联合类型实现

(1)在 TypeScript 中,可以使用联合类型实现函数重载。但联合类型有两个缺点:
  • 需要进行大量的逻辑判断,以缩小类型
  • 返回值类型仍然不确定,一般不推荐使用

(2)下面通过联合类型对 add 函数进行重构:
提示:使用联合类型实现函数重载时,需要在函数体内使用 typeof 进行类型判断,从而缩小类型范围,这样才能安全地调用特定类型的方法。
// 使用联合类型实现函数重载
function add(a1: number | string, a2: number | string) {
  if (typeof a1 === 'number' && typeof a2 === 'number') {
    // 类型缩小:此时 a1 和 a2 都是 number
    return a1 + a2;
  } else if (typeof a1 === 'string' && typeof a2 === 'string') {
    // 类型缩小:此时 a1 和 a2 都是 string
    return a1 + a2;
  }
}

// 调用 add 函数
console.log(add(10, 20));        // 输出: 30
console.log(add('coder', 'hangge')); // 输出: coderhangge

3,使用重载签名实现

(1)在 TypeScript 中,为了实现函数重载,可以编写不同的重载签名Overload Signature),表示函数可以以不同的方式进行调用。通常情况下,需要编写两个及以上的重载签名,再编写一个通用的函数实现。
提示:使用重载签名实现函数重载的好处是:编译器能够根据传入的参数类型,准确地推断出返回值的类型,无需手动进行类型转换。

(2)下面代码中,首先定义了两个函数的重载签名,接着编写通用的 sum 函数并实现相加逻辑。在调用 sum 函数时,它会根据传入的参数类型决定使用哪一个重载签名。
注意:重载签名的顺序很重要,TypeScript 会按照从上到下的顺序依次匹配重载签名。通常应该把更具体的签名放在前面。
// 1. 函数重载签名
function sum(a1: number, a2: number): number;
function sum(a1: string, a2: string): string;

// 2. 通用函数体
function sum(a1: any, a2: any): any {
  return a1 + a2;
}

// 3. 调用 sum 函数,可以根据参数类型自动匹配对应的签名
console.log(sum(20, 30));        // 输出: 50,返回类型为 number
console.log(sum('coder', 'hangge')); // 输出: coderhangge,返回类型为 string
评论

全部评论(0)

回到顶部