函数参数
本教程共 80 篇 · 第 18 篇 · 更新于 2026-08-10 · 约 9 分钟阅读
本节目标:学会处理可选参数、默认值、剩余参数和解构参数,让函数的参数列表既灵活又类型安全。
上一节讲了基础的类型注解。但要写出实用的函数,只靠必选参数是不够的。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 准确识别每种方式。