在 TypeScript 里给函数参数设默认值,写在参数后面用等号赋值即可:function f(a, b = 10)。要区分的是「可选参数 ?」和「默认参数 =」:前者允许不传、值为 undefined,后者在不传时自动补上你给的默认值。本文把规则和坑一次讲清。

你写一个函数,希望第二个参数可省略,省略时用一套默认配置。这时候有人写 ?,有人写 =,结果调用方拿到的值不一样,排查半天才发现语义不同。今天这篇文章,编程狮就把 TS 默认参数和可选参数的差别讲透,让你写函数时不再纠结。
一、先分清「可选」和「默认」两件事
可选参数用问号 ? 声明,含义是「调用时可以省掉这个参数」,省掉后它的值是 undefined。默认参数用等号 = 声明,含义是「省掉时自动填上这个值」。两者都能让参数变「可省」,但省掉之后拿到什么,完全不同。
用一个调用对比就清楚了:声明 function f(a: string, b?: string, c: string = "x"),调用 f("A") 时 b 是 undefined、c 是 "x";而如果把 b 写成 c?: string 却想让它「不传就有默认值」,那是做不到的——? 永远不会补默认值,这是两者最容易被混淆的点。
⚠️ 注意:可选参数和默认参数后面的位置有硬性约束——一旦某个参数带了
?或=,它后面的参数也必须带,否则编译报错。必填参数不能排在可省参数之后。
如果还没搭好 TS 环境,可以先过一遍 TypeScript 教程,把类型和函数基础过一遍。
二、方案一:用等号设默认参数,最直观
默认参数适合「有一个合理的兜底值」的场景。调用方不传,函数内部照样能拿到可用的值。
function greet(name: string, greeting: string = "你好") {
return `${greeting},${name}`;
}
console.log(greet("小明"));
console.log(greet("小明", "早上好"));
上面这段做了什么:第二个参数不传时,自动用 "你好";传了就用传进来的值。执行后你应该看到 你好,小明 和 早上好,小明。这里 greeting 永远是字符串,不会是 undefined。
默认值还可以引用前面的参数,这是它比 ? 更灵活的地方:比如 function pow(base: number, exp: number = base) 让指数默认等于底数,调用 pow(3) 就得到 3 的 3 次方。注意只能引用「排在它前面」的参数,反向引用编译器会报错,因为参数求值是从左到右进行的。
需要查语法细节,TypeScript 通义灵码教程 可以放在手边随时翻。

三、方案二:用问号声明可选参数
可选参数适合「这个参数可能根本不存在」的场景。注意它的值是 undefined,后续使用要先判空,否则可能踩到类型错误。
function log(tag?: string) {
const t = tag ?? "默认标签";
console.log(`[${t}] 执行`);
}
log();
log("支付");
这里 tag 不传就是 undefined,函数内部用 ?? 做了兜底。想了解新版能力可以看 TypeScript 5.0 新特性,新版本在参数和修饰符上有不少增强。
四、方案三:参数顺序与剩余参数的配合
当函数同时有必填、默认、可选、剩余参数时,必须按「必填 → 默认/可选 → 剩余」的顺序排。写反了编译器直接报错。
function build(path: string, opts: { gzip?: boolean } = {}, ...extra: string[]) {
console.log(path, opts, extra);
}
默认值也常用于对象解构,这也是日常写配置函数的高频写法:function cfg({ timeout = 3000, retry = 1 } = {}) {}——这里 = {} 让「整个对象不传」也安全,内部字段再各自带默认。注意解构默认和参数默认是两层:= {} 保的是「对象本身可省」,timeout = 3000 保的是「字段可省」,少写任一层都可能让调用方传空对象时拿到 undefined。
| 写法 | 不传时的取值 | 典型用途 |
|---|---|---|
a?: T |
undefined |
参数可能不存在,需自行判空 |
a: T = v |
默认值 v |
有合理兜底值 |
...rest: T[] |
空数组 | 收集剩余参数 |
如果本地跑起来遇到奇怪的类型报错,Trae 排障 TS 相关问题 里有一些真实案例分析,可以对照着看。
五、踩坑清单
三个最常翻车的地方,每个给现象、原因和办法。
- 现象:必填参数写在默认参数后面,编译直接红。原因:TS 要求可省参数不能排在必填之前。解决:调整顺序,必填永远在最前。
- 现象:函数里拿到 undefined 导致运行时报错。原因:用了
?却没判空。解决:要么改成=给默认值,要么用??兜底。 - 现象:默认参数依赖另一个参数的值,行为不符合预期。原因:默认值的求值顺序是按位置从左到右,后面的默认参数才能引用前面的。解决:确认引用次序,必要时在函数体里处理。
验证最直接:写一个同时带 ? 和 = 的函数,分别传 0 个、1 个、2 个参数,打印结果确认 undefined 和默认值各出现一次。预期结果(未在本机执行)是 ? 参数不传得 undefined、= 参数不传得默认值。
默认参数与类型推断的几个小细节
参数默认值的写法还会影响 TS 的类型推断:一旦写了 = 10,这个参数就被推断成 number,之后再想传字符串就得显式标联合类型,比如 function f(x: number | string = 10)。否则直接赋字符串默认值会被编译器拦下来。
另一点常被忽略:默认参数只在「参数被省略、或显式传 undefined」时才会生效。如果你传的是 null,默认值不会触发,null 就是 null。所以判断「调用方有没有传」,要用 === undefined,而不是宽松的假值判断,否则 0、空字符串这类合法值会被误当成没传。
把「参数默认值」当成函数自己的兜底逻辑,而不是调用方的义务,调用方就更自由:能省就省,要给也给得明确。
写完后 检查 一下调用方不传参时拿到的是不是默认值;如果 失败 地报类型错,多半是默认值类型和参数标注对不上,按标注补一致即可。
总结
TypeScript 的函数参数,默认参数 = 在不传时补默认值,可选参数 ? 不传时是 undefined,两者语义不同。写函数时按「必填 → 默认/可选 → 剩余」排好顺序,就能避免绝大多数编译错误。
要点带走:
=给兜底值,?只是允许省略;- 可省参数不能排在必填参数之后;
- 用
?后要记得判空或用??兜底。
下一步可以看看函数重载和类型守卫,把「根据参数形态走不同逻辑」这种模式也掌握住;重载配合默认参数,还能让你在「少传参数走默认分支、多传参数走特化分支」之间平滑切换,是写通用工具函数时的常用组合。
延伸学习
想把这块知识系统补齐,可以按这个顺序来:
- 先过一遍 Python 函数参数声明与调用示例,对照看不同语言里参数处理的异同;
- 函数参数底层传递机制可翻 数组作为函数参数的传递方式,理解值传递与引用传递。
常见问题
Q:可选参数和默认参数能混用吗?
A:能,但顺序要合规。一旦某个参数用了 ? 或 =,它后面的参数也必须可省或带默认,必填参数不能排在他们后面,否则编译报错。
Q:默认参数的值会每次重新计算吗?
A:默认值的表达式是按调用时从左到右求值的,且每次调用不传该参数都会重新求值。如果默认值是个开销很大的表达式,建议改在函数体里按需计算。
Q:为什么用了问号还是拿到 undefined?
A:这正是 ? 的语义——它只表示「可省略」,省略时值就是 undefined,不会自动补任何东西。想要兜底值就该用 = 声明默认参数。

TRAE-AI编程



