首页 / TypeScript 入门教程 / 函数参数

TypeScript 入门教程

函数参数

本教程共 80 篇 · 第 18 篇 · 更新于 2026-08-10 · 约 9 分钟阅读

TypeScriptTypeScript 入门教程函数参数可选参数默认值

本节目标:学会处理可选参数、默认值、剩余参数和解构参数,让函数的参数列表既灵活又类型安全。

上一节讲了基础的类型注解。但要写出实用的函数,只靠必选参数是不够的。JavaScript 函数本来就支持可变数量的参数,TypeScript 提供了对应的类型工具来安全地表达这种灵活性。

可选参数

在参数名后面加个问号 ?,这个参数就变成可选的了:

function buildName(firstName: string, lastName?: string): string {
  if (lastName) {
    return firstName + " " + lastName;
  }
  return firstName;
}

console.log(buildName("Alice"));          // "Alice"
console.log(buildName("Alice", "Smith")); // "Alice Smith"

可选参数的本质是:它的类型自动变成了 原始类型 | undefined。所以 lastName 在上面的例子中实际类型是 string | undefined,你必须先检查它是否存在再使用。

Important

可选参数必须放在必选参数的后面。写成 function fn(a?: string, b: string) 会直接报错,因为调用 fn("hello") 时,TS 不知道这个参数到底是给了 a 还是 b

// ❌ 错误:可选参数不能在必选参数前面
function buildName(firstName?: string, lastName: string) {
  //                                                   ~~~~ 必选参数不能跟在可选参数后面
}

// ✅ 正确:可选参数放后面
function buildName(firstName: string, lastName?: string) {
  // OK
}

有一点需要特别注意:参数标记为可选,和你手动写成 string | undefined 是不一样的。

function f1(x?: number) {
  // x 的类型是 number | undefined
}
f1();      // ✅ 可以不传
f1(10);    // ✅ 可以传值
f1(undefined); // ✅ 可以显式传 undefined

function f2(x: number | undefined) {
  // x 的类型是 number | undefined
}
f2();      // ❌ 必须传参数,虽然可以传 undefined
f2(10);    // ✅
f2(undefined); // ✅

区别在于:? 让参数可以整个不传,而手动写 | undefined 只是允许传入 undefined,但参数本身还是必须传的。

默认值参数

给参数设默认值,它就会自动变成可选的——而且类型推断也更聪明:

function greet(name: string, greeting: string = "Hello"): string {
  return greeting + ", " + name + "!";
}

console.log(greet("Alice"));            // "Hello, Alice!"
console.log(greet("Bob", "Hi"));        // "Hi, Bob!"

greeting 的类型被推断为 string,不是 string | undefined。因为默认值保证了它永远不可能是 undefined——就算你不传,TS 也知道它会被赋值为 "Hello"

你甚至可以省略类型注解,让 TypeScript 从默认值推断:

function getScore(level = 1) {
  // level 被推断为 number
  return level * 100;
}
Note

默认值参数不能同时标记为可选。function fn(x?: number = 10) 会报错,因为默认值已经隐含了可选,不需要再加 ?

默认值参数的位置比可选参数更灵活——它可以出现在必选参数前面:

// 默认值参数可以放在必选参数前面
// 但这通常意味着调用时你可能需要传 undefined 来触发默认值
function createUser(name = "Anonymous", age: number): void {
  console.log(name, age);
}

createUser(undefined, 25); // 显式传 undefined 触发默认值
createUser("Alice", 30);   // 正常使用

虽然语法上允许这样写,但实际中用得很少——因为调用方的体验不太好。大多数情况下把默认值参数放最后是最自然的。

剩余参数

当参数数量不确定时,用 ... 语法收集剩余参数:

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

console.log(sum(1, 2, 3));       // 6
console.log(sum(10, 20, 30, 40)); // 100

剩余参数的类型必须是数组类型——T[]Array<T>。因为剩余参数拿到的永远是一个数组。

你也可以在剩余参数前面放普通参数:

function join(separator: string, ...values: string[]): string {
  return values.join(separator);
}

console.log(join(", ", "a", "b", "c")); // "a, b, c"
console.log(join(" - ", "x", "y"));     // "x - y"

剩余参数必须放在参数列表的最后一位,这是 JavaScript 语法本身就要求的。

参数解构的类型写法

JavaScript 允许把对象直接解构成参数,TypeScript 中给解构参数加类型的写法稍微绕一点——类型注解写在解构模式后面:

// 类型注解跟在解构语法后面
function printPoint({ x, y }: { x: number; y: number }): void {
  console.log(`(${x}, ${y})`);
}

printPoint({ x: 10, y: 20 }); // "(10, 20)"

如果你觉得内联类型太冗长,提取一个类型别名是更好的做法:

type Point = { x: number; y: number };

function printPoint({ x, y }: Point): void {
  console.log(`(${x}, ${y})`);
}

数组解构也一样:

function headAndTail([head, ...tail]: [number, ...number[]]): void {
  console.log("Head:", head);
  console.log("Tail:", tail);
}

headAndTail([1, 2, 3, 4]);
// Head: 1
// Tail: [2, 3, 4]

对于对象解构,还可以给解构出来的变量加默认值:

function drawShape({ color = "black", size = 1 }: { color?: string; size?: number } = {}): void {
  console.log(`Drawing ${color} shape of size ${size}`);
}

drawShape();                       // "Drawing black shape of size 1"
drawShape({ color: "red" });       // "Drawing red shape of size 1"
drawShape({ color: "blue", size: 3 }); // "Drawing blue shape of size 3"

这里有两个默认值:解构变量的默认值(color = "black")和整个参数的默认值(= {})。后者让函数可以零参数调用——如果不传参,{} 会作为默认参数传入,然后解构的默认值各自生效。

小结

可选参数用 ?、默认值参数用 = value、剩余参数用 ...args: T[]、解构参数的类型写在模式后面。掌握了这四种写法,你就能写出既灵活又类型安全的函数签名了。下一节我们讲函数重载——当一个函数有多种调用方式时,怎么让 TypeScript 准确识别每种方式。