TypeScript 函数参数默认值怎么设?可选参数与默认值分清

编程狮(w3cschool.cn) 2026-10-15 07:09:09 浏览数 (91)
反馈

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

TypeScript 参数默认值对比图

你写一个函数,希望第二个参数可省略,省略时用一套默认配置。这时候有人写 ?,有人写 =,结果调用方拿到的值不一样,排查半天才发现语义不同。今天这篇文章,编程狮就把 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 通义灵码教程 可以放在手边随时翻。

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,两者语义不同。写函数时按「必填 → 默认/可选 → 剩余」排好顺序,就能避免绝大多数编译错误。

要点带走:

  • = 给兜底值,? 只是允许省略;
  • 可省参数不能排在必填参数之后;
  • 用 ? 后要记得判空或用 ?? 兜底。

下一步可以看看函数重载和类型守卫,把「根据参数形态走不同逻辑」这种模式也掌握住;重载配合默认参数,还能让你在「少传参数走默认分支、多传参数走特化分支」之间平滑切换,是写通用工具函数时的常用组合。

延伸学习

想把这块知识系统补齐,可以按这个顺序来:

  1. 先过一遍 Python 函数参数声明与调用示例,对照看不同语言里参数处理的异同;
  2. 函数参数底层传递机制可翻 数组作为函数参数的传递方式,理解值传递与引用传递。

常见问题

Q:可选参数和默认参数能混用吗?

A:能,但顺序要合规。一旦某个参数用了 ? 或 =,它后面的参数也必须可省或带默认,必填参数不能排在他们后面,否则编译报错。

Q:默认参数的值会每次重新计算吗?

A:默认值的表达式是按调用时从左到右求值的,且每次调用不传该参数都会重新求值。如果默认值是个开销很大的表达式,建议改在函数体里按需计算。

Q:为什么用了问号还是拿到 undefined?

A:这正是 ? 的语义——它只表示「可省略」,省略时值就是 undefined,不会自动补任何东西。想要兜底值就该用 = 声明默认参数。

0 人点赞