TypeScript: TypeScript 函数类型

最后更新:2026-08-26

函数是 JavaScript 的核心构造,TypeScript 为函数的参数和返回值添加了完整的类型系统——让函数的"输入"和"输出"都有据可查。

1. 函数类型注解

(1) 参数和返回值类型

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

let result = add(1, 2);    // result 推断为 number
// add("1", "2");          // ❌ 参数类型不匹配
// add(1);                 // ❌ 参数数量不够

(2) 返回值类型推断

TypeScript 能根据 return 语句推断返回值类型,但建议显式标注:

TYPESCRIPT
// 返回值推断——能推断但不推荐(大函数容易推断错误)
function multiply(a: number, b: number) {
  return a * b;   // 推断返回 number
}

// 显式标注——推荐(文档作用 + 编译时捕获错误)
function divide(a: number, b: number): number {
  if (b === 0) throw new Error("除数不能为零");
  return a / b;
}

(3) void 返回值

函数没有返回值时用 void

TYPESCRIPT
function log(message: string): void {
  console.log(message);
  // 没有 return 语句,或者 return; —— 都是 void
}

(4) never 返回值

函数永远不会有返回值时用 never——要么抛异常,要么无限循环:

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

function infiniteLoop(): never {
  while (true) { }
}
返回类型 含义
void 函数正常结束,但没有返回值
never 函数永远不会结束(抛异常或死循环)

▶ 示例:带类型的工具函数

TYPESCRIPT
function clamp(value: number, min: number, max: number): number {
  return Math.min(Math.max(value, min), max);
}

function formatCurrency(amount: number, symbol: string = "¥"): string {
  return symbol + amount.toFixed(2);
}

console.log(clamp(150, 0, 100));           // 100
console.log(clamp(-5, 0, 100));            // 0
console.log(clamp(50, 0, 100));            // 50
console.log(formatCurrency(99.5));         // ¥99.50
console.log(formatCurrency(42, "$"));      // $42.00
▶ 试一试

输出:

TEXT 📖 仅展示
100
0
50
¥99.50
$42.00

2. 可选参数与默认参数

(1) 可选参数 ?

TYPESCRIPT
function greet(name: string, title?: string): string {
  if (title) {
    return `${title} ${name}`;
  }
  return `你好,${name}`;
}

console.log(greet("Charlie"));           // "你好,Charlie"
console.log(greet("Charlie", "工程师"));  // "工程师 Charlie"
⚠️ 规则:可选参数必须在必选参数后面。function greet(title?: string, name: string) 会报错。

(2) 默认参数

TYPESCRIPT
function createUser(name: string, role: string = "viewer", active: boolean = true): string {
  return `${name},角色:${role},${active ? "活跃" : "未激活"}`;
}

console.log(createUser("Charlie"));                       // Charlie,角色:viewer,活跃
console.log(createUser("Diana", "editor"));             // Diana,角色:editor,活跃
console.log(createUser("Eric", "admin", false));       // Eric,角色:admin,未激活

(3) 可选参数 vs 默认参数

特性 可选参数 title?: string 默认参数 role: string = "viewer"
调用时 可省略,值为 undefined 可省略,值为默认值
类型 string | undefined string(不是 undefined 联合)
推荐 值确实可能为 undefined 大多数情况——有合理默认值更好

3. 剩余参数

剩余参数用 ... 语法收集多余参数为数组:

TYPESCRIPT
function sum(first: number, ...rest: number[]): number {
  return rest.reduce((total, n) => total + n, first);
}

console.log(sum(1));             // 1
console.log(sum(1, 2));         // 3
console.log(sum(1, 2, 3, 4));   // 10

(1) 剩余参数必须是数组类型

TYPESCRIPT
function logAll(prefix: string, ...messages: string[]): void {
  messages.forEach(msg => console.log(`${prefix}: ${msg}`));
}

logAll("DEBUG", "启动服务", "连接数据库", "就绪");

输出:

TEXT 📖 仅展示
DEBUG: 启动服务
DEBUG: 连接数据库
DEBUG: 就绪

4. 函数类型表达式

函数也可以作为值传递——这时需要用函数类型表达式描述类型:

(1) 类型别名定义函数类型

TYPESCRIPT
type MathOperation = (a: number, b: number) => number;

let add: MathOperation = (a, b) => a + b;
let subtract: MathOperation = (a, b) => a - b;
let multiply: MathOperation = (a, b) => a * b;

console.log(add(10, 5));       // 15
console.log(subtract(10, 5));  // 5
console.log(multiply(10, 5));  // 50

(2) 回调函数类型

函数类型最常见的用途——描述回调函数的签名:

TYPESCRIPT
function fetchData(url: string, onSuccess: (data: string) => void, onError: (error: Error) => void): void {
  // 模拟异步操作
  if (url.startsWith("https://")) {
    onSuccess("数据加载成功");
  } else {
    onError(new Error("仅支持 HTTPS"));
  }
}

fetchData(
  "https://api.example.com",
  data => console.log(data),         // ✅ (data: string) => void
  error => console.log(error.message) // ✅ (error: Error) => void
);

(3) 用接口定义函数类型

TYPESCRIPT
interface Comparator {
  (a: number, b: number): number;
}

let ascending: Comparator = (a, b) => a - b;
let descending: Comparator = (a, b) => b - a;

let nums = [3, 1, 4, 1, 5];
console.log([...nums].sort(ascending));   // [1, 1, 3, 4, 5]
console.log([...nums].sort(descending));  // [5, 4, 3, 1, 1]

5. 函数重载

函数重载让同一个函数名支持不同的参数类型组合——TypeScript 的重载是"声明 + 实现"的模式:

(1) 基本语法

TYPESCRIPT
// 重载签名——描述每种参数组合的返回类型
function format(value: number): string;
function format(value: string): string;
function format(value: Date): string;

// 实现签名——必须兼容所有重载签名
function format(value: number | string | Date): string {
  if (typeof value === "number") {
    return value.toFixed(2);
  } else if (typeof value === "string") {
    return value.trim();
  } else {
    return value.toISOString();
  }
}

console.log(format(3.14));             // "3.14"
console.log(format("  hello  "));      // "hello"
console.log(format(new Date()));       // "2024-..."

(2) 重载的顺序

TypeScript 按重载声明的顺序从上到下匹配——更具体的重载应放在前面:

TYPESCRIPT
// ✅ 正确顺序——具体的在前
function process(value: string): string;
function process(value: any): unknown;

// ❌ 错误顺序——any 会匹配所有参数,后面的重载永远不会被调用
// function process(value: any): unknown;
// function process(value: string): string;

▶ 示例:重载实现类型安全的事件处理

TYPESCRIPT
function on(event: "click", handler: (x: number, y: number) => void): void;
function on(event: "keydown", handler: (key: string) => void): void;
function on(event: string, handler: Function): void {
  console.log(`注册事件:${event}`);
  // 实际实现省略...
}

// 调用时自动获得正确的参数类型
on("click", (x, y) => {
  console.log(`点击位置:(${x}, ${y})`);   // x, y 推断为 number
});

on("keydown", (key) => {
  console.log(`按下按键:${key}`);           // key 推断为 string
});
▶ 试一试

6. 箭头函数的类型

(1) 箭头函数的类型注解

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

// 作为回调时,参数类型通常可以推断
const nums = [1, 2, 3];
const doubled = nums.map(n => n * 2);       // n 推断为 number
const asStrings = nums.map(n => String(n)); // n 推断为 number

(2) 箭头函数与 this

箭头函数不绑定自己的 this——它继承外层的 this

TYPESCRIPT
class Timer {
  seconds = 0;

  start() {
    // 箭头函数——this 指向 Timer 实例
    setInterval(() => {
      this.seconds++;
      console.log(`${this.seconds}秒`);
    }, 1000);
  }

  startBroken() {
    // 普通函数——this 指向全局或 undefined(严格模式)
    setInterval(function () {
      // this.seconds++;  // ❌ 运行时 this 不是 Timer
    }, 1000);
  }
}

❓ 常见问题

Q 函数返回值类型要不要显式标注?
A 建议标注。虽然 TypeScript 能推断,但显式标注有三个好处:(1) 文档作用——一眼看出返回类型 (2) 防止意外返回错误类型 (3) 更好的错误定位——报错在函数签名而非调用处。小函数(单行 return)可以省略,超过3行的函数建议标注。
Q void 和 undefined 返回值有什么区别?
A void 表示"函数不关心返回值"——函数可以有 return 语句也可以没有,调用者不应该使用返回值。undefined 是具体的类型——函数明确返回 undefined。实际开发中,没有返回值的函数都用 void。
Q 函数重载和联合类型参数有什么区别?
A 联合类型 function fn(x: string | number) 只能描述"参数可以是这些类型之一",但所有参数组合返回同一类型。重载可以描述"不同参数组合返回不同类型"——这是联合类型做不到的。如果只是参数类型灵活但返回类型统一,用联合类型更简洁;需要不同返回类型时用重载。
Q 默认参数和可选参数能同时用吗?
A 不能。function fn(x?: string = "hi") 是语法错误——默认参数已经隐含了"可省略"的含义,不需要再加 ?x: string = "hi" 本身就等于 x?: string 但值是 "hi" 而不是 undefined。

📖 小节

📝 作业

  1. 基础题(难度⭐):写三个函数——add(a, b) 加法、subtract(a, b) 减法、multiply(a, b) 乘法,参数和返回值都是 number。用函数类型表达式统一声明类型。
  2. 进阶题(难度⭐⭐):写一个函数 buildQuery(params: Record<string, string | number | boolean>),把对象转成 URL 查询字符串。例如 { page: 1, size: 10, active: true }"page=1&size=10&active=true"
  3. 挑战题(难度⭐⭐⭐):用函数重载实现 createElement(tag, props):当 tag 为 "input" 时 props 必须有 type: string,当 tag 为 "a" 时 props 必须有 href: string,当 tag 为 "div" 时 props 是通用对象。每种情况返回不同的类型描述对象。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏