TypeScript 函数(深入浅出)

TypeScript 函数:从入门到实战

在现代前端开发中,TypeScript 已经成为构建大型应用的标配语言。它不仅提供了静态类型检查,还让代码更健壮、可维护性更强。而函数作为编程的核心单元,是 TypeScript 中最常用也最重要的部分之一。掌握好 TypeScript 函数,就等于掌握了代码复用和逻辑抽象的钥匙。

想象一下,你正在搭建一座高楼。函数就像是预制的钢筋混凝土模块——你只需定义一次,就能在多个楼层重复使用,既节省时间,又保证质量。TypeScript 函数则更进一步,它不仅让你“搭得快”,还让你“搭得稳”。接下来,我们就从基础语法到高级用法,一步步拆解 TypeScript 函数的精髓。


函数的基本语法与类型声明

在 TypeScript 中,函数的定义比 JavaScript 更加严谨。你需要为参数和返回值明确指定类型,这就像给每个参数都贴上标签,让编译器在你写错时及时提醒。

// 定义一个接收两个数字参数,返回它们和的函数
function add(a: number, b: number): number {
  return a + b;
}

// 调用函数
const result = add(5, 3); // result 的值是 8

注释:这里的 a: numberb: number 是参数类型注解,表示这两个参数必须是数字类型。: number 在函数名后表示该函数返回一个数字。这种写法让函数的用途一目了然,也避免了传入字符串导致的运行时错误。

如果你不写类型,TypeScript 会尝试推断,但为了代码的可读性和安全性,强烈建议显式声明类型。尤其是在团队协作中,清晰的类型声明能极大降低沟通成本。


可选参数与默认参数

现实世界中,很多函数的参数并非总是必需的。比如一个“用户注册”函数,可能需要邮箱,但手机号是可选的。TypeScript 通过可选参数和默认参数来支持这种灵活性。

可选参数

在参数名后加 ?,表示该参数是可选的:

function greet(name: string, greeting?: string): string {
  // 如果没有提供 greeting,则使用默认值
  const message = greeting ? `${greeting}, ${name}!` : `Hello, ${name}!`;
  return message;
}

// 使用示例
console.log(greet("Alice")); // 输出: Hello, Alice!
console.log(greet("Bob", "Hi")); // 输出: Hi, Bob!

注释:greeting?: string 表示 greeting 是可选参数。在函数内部,我们通过 ? 操作符判断其是否存在。可选参数必须放在所有必选参数之后,这是 TypeScript 的强制规则。

默认参数

你也可以为可选参数设置默认值,让调用更简洁:

function createUser(name: string, age: number = 18, isActive: boolean = true): object {
  return {
    name,
    age,
    isActive
  };
}

// 使用示例
console.log(createUser("Charlie")); 
// 输出: { name: "Charlie", age: 18, isActive: true }

console.log(createUser("Diana", 25, false)); 
// 输出: { name: "Diana", age: 25, isActive: false }

注释:age: number = 18 表示如果调用时没有传入 age,则默认为 18。默认参数可以放在必选参数之后,也可以放在可选参数之前,但必须在可选参数之后。


函数类型与类型别名

在 TypeScript 中,函数本身也可以被当作类型来使用。这在高阶函数、回调函数或事件处理中非常有用。

函数类型表达式

你可以用 (): 返回类型 的形式定义一个函数类型:

// 定义一个函数类型:接收两个 number,返回一个 number
type MathOperation = (a: number, b: number) => number;

// 使用这个类型定义函数
const multiply: MathOperation = (x, y) => x * y;

// 也可以作为参数类型
function calculate(operation: MathOperation, a: number, b: number): number {
  return operation(a, b);
}

// 调用
console.log(calculate(multiply, 4, 5)); // 输出: 20

注释:type MathOperation = (a: number, b: number) => number; 定义了一个函数类型别名。=> 左边是参数列表,右边是返回值类型。这样,multiply 函数就符合这个类型,可以安全地传给 calculate

类型别名 vs 接口

虽然接口(interface)也能定义函数类型,但类型别名(type)更灵活,尤其适合复杂函数签名。推荐在函数类型场景中优先使用 type


高阶函数与回调函数

高阶函数是函数式编程的核心思想之一:函数可以作为参数传递,也可以作为返回值。TypeScript 通过类型系统让这类编程模式更加安全。

回调函数示例

假设你有一个数据处理函数,它会接收一个函数作为“处理逻辑”:

// 定义一个高阶函数,接收一个处理函数作为参数
function processArray(numbers: number[], processor: (n: number) => number): number[] {
  return numbers.map(processor);
}

// 定义一个处理逻辑:将数字平方
function square(n: number): number {
  return n * n;
}

// 调用高阶函数
const input = [1, 2, 3, 4];
const result = processArray(input, square);

console.log(result); // 输出: [1, 4, 9, 16]

注释:processor: (n: number) => number 是一个函数类型,表示它接受一个数字并返回一个数字。map 方法会遍历数组,对每个元素调用 processor。这使得 processArray 可以灵活应对不同的处理逻辑。

箭头函数简化语法

TypeScript 支持箭头函数,让代码更简洁:

// 使用箭头函数作为回调
const doubled = processArray([1, 2, 3], (x) => x * 2);
console.log(doubled); // 输出: [2, 4, 6]

注释:箭头函数 x => x * 2 等价于 function(x) { return x * 2; }。在 TypeScript 中,箭头函数的类型推断能力很强,通常无需手动声明类型。


重载函数:同一个函数,多种用法

有时候,你希望一个函数能处理多种不同的输入方式。比如,一个 formatDate 函数,可能需要接收 Date 对象、字符串或数字。

TypeScript 支持函数重载,允许你为同一个函数名定义多个签名,编译器会根据调用时的参数自动选择最合适的版本。

// 函数重载签名:接收 Date 对象
function formatDate(date: Date): string;

// 函数重载签名:接收字符串
function formatDate(dateString: string): string;

// 函数重载签名:接收数字(时间戳)
function formatDate(timestamp: number): string;

// 实际实现(必须兼容所有重载签名)
function formatDate(date: Date | string | number): string {
  const d = new Date(date);
  return d.toLocaleDateString();
}

// 使用示例
console.log(formatDate(new Date())); // 日期格式化输出
console.log(formatDate("2024-04-05")); // 字符串也支持
console.log(formatDate(1712345678000)); // 时间戳也支持

注释:重载函数的实现必须能处理所有重载签名的类型。date: Date | string | number 是联合类型,表示它可以是其中任意一种。重载让函数更“聪明”,用户无需关心内部逻辑,只需传入合适参数即可。


TypeScript 函数的最佳实践

掌握语法只是第一步,写出“好”的 TypeScript 函数才是关键。以下是几个实用建议:

  1. 始终声明类型:哪怕编译器能推断,也要显式写上,提升可读性。
  2. 避免过长函数:单个函数最好不超过 20 行,职责单一。
  3. 使用命名函数:比起匿名函数,命名函数在错误堆栈中更易定位。
  4. 合理使用默认参数:避免让函数签名太复杂,但也要防止参数过多。
  5. 重用函数类型:用 type 定义通用函数类型,提高代码复用。

总结

TypeScript 函数不仅是代码的“零件”,更是逻辑的“骨架”。它通过类型系统让你写出更安全、更清晰、更易维护的代码。从基础语法到高阶用法,每一步都值得深入理解。

无论是定义一个简单的加法函数,还是构建复杂的回调处理逻辑,TypeScript 函数都能为你提供强大的支持。掌握它,你就能在复杂的项目中游刃有余,写出既高效又可靠的代码。

当你下次写一个函数时,不妨多花 10 秒思考:它的类型是什么?参数是否可选?是否需要重载?这些细节,正是专业开发者的分水岭。