学径XUEJING · 个人学习平台通知设置

课程库 TypeScript TypeScript 起步 › 第 3

函数的类型 —— 参数、返回值、可选参数

普通课50 分钟

本课导读

上一课定了分寸:"边界即合同,合同要白纸黑字"——而代码里最重要的边界就是函数签名。本课把函数的类型写讲究:参数与返回值、"没有返回值"的 void、可选参数 ? 与默认值参数 = 的分工,最后认识箭头函数(现代 JS/TS 满地都是它)。学完后,你看任何函数的第一行就能读出它的完整合同。预计 50 分钟。

完整的函数签名

TypeScript

一行签名三层信息:收什么(两个 number)、吐什么(number)、连起来读就是这个函数的完整合同。上一课说返回值类型"通常可推断",那为什么还建议给正式函数写上?两个理由:

  • 签名即文档:读代码的人(包括三个月后的你、包括 AI)看第一行就懂,不用钻进函数体
  • 提前锁定意图:写了 : number,函数体里手滑 return price + "元" 会当场报错在函数内部——不写的话,错误类型会被推断出去,在几百行外的使用处才炸

void:说好了不交货

有些函数只干活不交货(打印日志、发通知)。"没有返回值"也是一种类型——void

TypeScript

眼熟吗?Python 单元里"没写 return 的函数返回 None、接住 .sort() 的结果是 None"那类事故——TS 用 void 把它变成编译期问题:想把"不交货"当货用,代码跑都跑不起来。

可选参数 ? 与默认值参数 =

调用者不一定每个参数都想传。两种设计,分工不同:

TypeScript

怎么选?口诀:有合理兜底值 → 默认值参数(dept 不传就是总部,干脆利落);"没传"本身是个有意义的状态 → 可选参数(没指定渠道走站内信,逻辑分叉由你控制)。两条通用规矩:

  • 可选/默认值参数都必须排在必填参数后面function f(a?: string, b: number) 报错——和 Python 同款规矩,跨语言通用)
  • 可选参数在函数体内的类型是 string | undefined——竖线是什么?下下课"联合类型"正式登场,这里先脸熟

箭头函数:函数的简写形态

现代 JS/TS 代码里,你会大量遇到另一种函数写法——箭头函数

TypeScript

三个认知就够用:

  • (参数) => 表达式 是函数的简写;参数类型标注规则与普通函数一致
  • 注意 map 里的 (p) => p * 0.8 没写 p 的类型却没报错——TS 从 pricesnumber[] 推断出 p 是 number(推断连回调参数都罩着,这叫上下文推断)
  • map(整列加工)和 filter(按条件筛选)——如果你学过 Python 的列表推导式,这就是 JS 世界的同一个思想;没学过也没关系,把它们当"数组的批量操作"即可

✍️ 练习

练习 1:补签名

给下面的函数补上完整的类型签名(参数 + 返回值),让三处调用都通过检查。

TypeScript
✅ 参考答案
function shippingFee(weight: number, express: boolean): number {
    const base = weight * 5;
    if (express) {
        return base + 12;
    }
    return base;
}

console.log(shippingFee(2, false));
console.log(shippingFee(2, true));

起始代码的报错 TS7006(参数隐式 any)值得认识:strict 模式下 TS 拒绝"参数没类型"——这正是"边界必须白纸黑字"被写进了编译器规则。

练习 2:选对工具

设计函数 bookRoom:房间号必填(字符串);人数常见是 4 人;备注可有可无、有备注时要附在结果里。判断:人数和备注各该用"默认值参数"还是"可选参数"?实现并测试三种调用。

TypeScript
✅ 参考答案
function bookRoom(room: string, people: number = 4, note?: string): string {
    let result = room + " 会议室," + people + " 人";
    if (note !== undefined) {
        result = result + "(" + note + ")";
    }
    return result;
}

console.log(bookRoom("A03"));
console.log(bookRoom("A03", 8));
console.log(bookRoom("A03", 8, "需要投影仪"));

选型复核:人数有合理兜底(4 人)→ 默认值;备注"没传"本身就是一种状态(不附加)→ 可选参数 + undefined 判断。

练习 3:箭头函数三连(挑战)

用 map / filter 和箭头函数完成三问:① 全部价格打八折 ② 筛出打折后仍超过 400 的 ③ (组合技)打折后仍超 400 的有几件?全程不写 for 循环。

TypeScript
✅ 参考答案
const prices = [599, 149, 1599, 459, 89];

const discounted = prices.map((p) => p * 0.8);
const stillPricey = discounted.filter((p) => p > 400);
const count = discounted.filter((p) => p > 400).length;

console.log(discounted);
console.log(stillPricey);
console.log("打折后仍超 400 的有 " + count + " 件");

链式组合 map(...).filter(...).length 是 JS/TS 数据处理的日常体态——而且注意:整条链上你没写一个类型,推断全程护航。

📝 随堂测验

随堂测验0 / 5 题正确

1. function f(a: number): string 这行签名读出的“合同”是?

2. 返回类型 void 的函数,其返回值被拿去参与运算会怎样?

3. “人数不传就按 4 人”和“备注不传就不附加”,分别用什么?

4. prices.map((p) => p * 0.8) 里 p 没标类型却不报错,因为?

5. map 和 filter 的分工是?

本课小结

  • 函数签名 = 完整合同:参数必写,正式函数返回值也写(签名即文档 + 错误锁定在函数内部)
  • void = 说好不交货;把"误用无返回值"变成编译期错误
  • 默认值参数(有兜底值)vs 可选参数 ?("没传"有意义);都排必填之后
  • 箭头函数 (p) => 表达式:数组方法的标配;上下文推断连回调参数都罩着

下一课处理更立体的数据:对象怎么标类型?接口(interface)登场——给数据的"形状"签合同。

划选正文任意文字可高亮、批注或加入复习卡

讨论

载入中…