函数基础:参数和返回类型

函数基础:参数和返回类型

欢迎继续本专栏的第九篇文章。在前几期中,我们已逐步深化了对 TypeScript 类型系统的认识,包括基本类型、特殊类型、枚举、类型断言,以及数组、元组和对象的管理。今天,我们将转向函数这一核心构建块,重点探讨函数的基础知识,特别是类型签名、参数和返回类型的定义。这部分内容是理解 TypeScript 如何提升函数可靠性的关键。我们将从函数的基本概念入手,逐步引入参数类型、返回类型、可选参数、默认参数和函数重载的概念,并通过丰富示例和实际场景分析,帮助您编写更健壮的函数代码。内容将由浅入深展开,确保您能从简单示例过渡到复杂应用,同时获得深刻的洞见。

理解函数在 TypeScript 中的定位

在编程中,函数是代码复用和逻辑封装的基本单位。JavaScript 中的函数灵活但动态,容易因参数类型不匹配或返回意外值而引发运行时错误。TypeScript 通过引入类型签名(type signature),为函数添加了静态检查层,这让函数成为类型安全的堡垒。类型签名本质上是函数的"合同":它定义了输入(参数类型)、输出(返回类型)和行为约束。

为什么函数类型如此重要?在大型项目中,函数往往被多处调用。没有类型,修改一个函数可能引发连锁 bug;有了类型,编译器能在变更时立即反馈。根据 TypeScript 社区的经验,使用函数类型能将相关错误减少 20-30%。函数在 TypeScript 中的定位不仅是执行代码,更是类型系统的桥梁:它连接变量、对象和更高级结构如类和泛型(后续文章详述)。

TypeScript 函数类型借鉴了函数式语言如 Haskell 的理念,但保持与 JavaScript 的兼容。任何 JS 函数都是有效的 TS 函数,但添加类型能带来智能提示、重构支持和文档化。需要注意的是,函数类型在运行时被移除,不会影响性能。我们将从最简单的函数签名开始,逐步扩展到高级特性,确保您能逐步掌握如何编写可靠的函数代码。

函数的基本定义与类型签名

让我们从函数的基础语法入手。TypeScript 函数的定义类似于 JavaScript,但添加了类型注解。

函数声明的基本形式

一个简单的无参数函数:

typescript 复制代码
function greet(): void {
  console.log("Hello, TypeScript!");
}

这里,(): void 是类型签名:空参数,返回 void(无值)。调用 greet() 时,编译器确保无参数传入。

带参数的函数:

typescript 复制代码
function add(a: number, b: number): number {
  return a + b;
}

签名 (a: number, b: number): number 指定两个 number 参数,返回 number。如果调用 add("1", 2),编译器报错:字符串不可赋值为 number。

函数表达式类似:

typescript 复制代码
const multiply: (x: number, y: number) => number = (x, y) => x * y;

这里,类型签名作为变量类型,箭头函数体匹配它。

类型签名的必要性在于它充当文档:阅读者一眼知函数需求。同时,IDE 如 VS Code 提供参数提示,提升开发效率。

类型签名的组成部分

类型签名包括:

  1. 参数列表:每个参数的名称和类型,如 a: number。

  2. 返回类型:函数输出的类型,如 : number。

  3. 可选的函数类型:用于变量或参数,如 (param: string) => boolean。

在 tsconfig.json 的 strict 模式下,未指定返回类型会推断,但显式声明推荐用于清晰性。

简单示例扩展:考虑一个处理字符串的函数。

typescript 复制代码
function capitalize(text: string): string {
  return text.charAt(0).toUpperCase() + text.slice(1);
}

如果返回非 string,如 number,编译错误。这确保函数行为一致。

通过这些基础,您可以看到类型签名如何将函数从"黑盒"转为"透明合同"。

参数类型:确保输入的安全性

参数是函数的输入,TypeScript 通过类型注解锁定它们,防止无效数据进入。

基本参数类型

参数类型直接注解在名称后。

typescript 复制代码
function describePerson(name: string, age: number): string {
  return `${name} is ${age} years old.`;
}

调用 describePerson("Alice", 30) 有效;describePerson(30, "Alice") 报错:参数顺序和类型必须匹配。

多参数场景:

typescript 复制代码
function calculateArea(length: number, width: number, unit: string = "sq ft"): string {
  return `${length * width} ${unit}`;
}

这里引入默认值(稍后详述),但类型仍指定。

参数类型支持联合:

typescript 复制代码
function logValue(value: string | number): void {
  console.log(value);
}

这允许灵活输入,但内部需处理类型(用 typeof 守卫)。

参数类型的深入应用

在复杂参数中,用接口定义形状。

typescript 复制代码
interface Point {
  x: number;
  y: number;
}

function distance(p1: Point, p2: Point): number {
  return Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2);
}

这在几何计算中实用,确保参数有正确属性。

数组参数:

typescript 复制代码
function sumArray(numbers: number[]): number {
  return numbers.reduce((acc, curr) => acc + curr, 0);
}

防止传入非数组或混合类型。

参数类型的益处:及早错误检测。在团队中,它减少沟通:函数签名即规格。

陷阱:参数过多表示需重构为对象。

返回类型:定义输出的预期

返回类型指定函数输出,确保调用者得到预期值。

基本返回类型

如 add 示例,返回 number。

无返回用 void:

typescript 复制代码
function logError(message: string): void {
  console.error(message);
}

如果添加 return,编译错误,除非 return undefined(但不推荐)。

推断返回:TypeScript 可自动推断,但显式更好。

typescript 复制代码
function getLength(text: string) {  // 推断 :string
  return text.length;  // 错误:返回 number,但推断为 number
}

修正为 : number。

返回类型的深入应用

返回联合:

typescript 复制代码
function findItem(id: number): string | undefined {
  // 逻辑
  if (found) return "item";
  return undefined;
}

这处理可选结果。

返回 Promise:

typescript 复制代码
async function fetchData(url: string): Promise<{ data: string }> {
  const res = await fetch(url);
  return { data: await res.text() };
}

在异步中,确保类型匹配。

返回类型提升可靠性:调用者知输出,可安全链式调用。

高级:返回 never 用于不返回函数(如 throw)。

可选参数:处理灵活输入

可选参数允许函数接受或忽略某些输入,用 ? 标记。

可选参数的基本用法

typescript 复制代码
function greetUser(name: string, title?: string): string {
  return title ? `${title} ${name}` : name;
}

调用 greetUser("Alice") 或 greetUser("Alice", "Ms.") 有效。title 默认为 undefined。

位置重要:可选参数后不能有必选。

typescript 复制代码
// function bad(a?: number, b: number): void {}  // 错误

正确:必选在前。

可选参数的深入应用

结合默认值:

typescript 复制代码
function createUser(name: string, age?: number): { name: string; age: number | undefined } {
  return { name, age };
}

在 API 中:

typescript 复制代码
interface Options {
  timeout?: number;
  retries?: number;
}

function request(url: string, options?: Options): Promise<Response> {
  // 实现
}

这允许灵活配置。

可选参数与守卫:

内部检查:

typescript 复制代码
if (title !== undefined) { /* 使用 */ }

可选参数增加函数通用性,但过多可选需考虑重载(后述)。

风险:undefined 处理不当导致 bug。总是考虑默认行为。

默认参数:提供内置值

默认参数在 ES6 引入,TypeScript 支持并类型化。

默认参数的基本用法

typescript 复制代码
function multiply(a: number, b: number = 1): number {
  return a * b;
}

调用 multiply(5) 返回 5;multiply(5, 2) 返回 10。

类型从默认值推断,但可显式。

默认参数的深入应用

复杂默认:

typescript 复制代码
function buildQuery(params: { key: string; value: string }[] = []): string {
  return params.map(p => `${p.key}=${p.value}`).join("&");
}

函数默认:

typescript 复制代码
function process(data: string, transformer: (s: string) => string = s => s.toUpperCase()): string {
  return transformer(data);
}

这在管道处理中实用。

默认与可选结合:默认使可选更强大。

typescript 复制代码
function log(message: string, level: string = "info"): void {
  // ...
}

默认参数简化调用,减少 boilerplate。但默认值需简单,避免运行时副作用。

陷阱:默认在调用时求值,非定义时。

函数重载:处理多种签名

函数重载允许同一函数名有多个签名,基于参数选择实现。

函数重载的基本用法

重载签名在实现前定义。

typescript 复制代码
function combine(a: string, b: string): string;
function combine(a: number, b: number): number;
function combine(a: string | number, b: string | number): string | number {
  if (typeof a === "string" && typeof b === "string") {
    return a + b;
  } else if (typeof a === "number" && typeof b === "number") {
    return a + b;
  }
  throw new Error("Invalid types");
}

调用 combine("a", "b") 返回 string;combine(1, 2) 返回 number。IDE 基于参数提示返回。

重载签名不实现,仅类型;实现签名覆盖所有。

函数重载的深入应用

多参数重载:

typescript 复制代码
function format(value: string): string;
function format(value: number, decimals: number): string;
function format(value: string | number, decimals?: number): string {
  if (typeof value === "string") {
    return value.trim();
  } else {
    return value.toFixed(decimals ?? 2);
  }
}

这处理不同输入。

类方法重载类似。

箭头函数不支持直接重载,用函数声明或接口。

接口重载:

typescript 复制代码
interface Overloaded {
  (a: string): string;
  (a: number): number;
}

const func: Overloaded = (a: any) => a;

重载提升函数多态性,在库设计中常见,如 lodash。

风险:实现复杂,易出错。优先联合类型;重载用于返回不同。

实际应用:编写可靠函数代码

整合概念,构建实用函数。

场景1:数据处理函数

typescript 复制代码
function processData(data: unknown, validator?: (d: unknown) => boolean): string | never {
  if (validator && !validator(data)) {
    throw new Error("Invalid data");
  }
  return data as string;  // 断言后返回
}

场景2:配置函数

typescript 复制代码
interface Config {
  host: string;
  port?: number;
}

function connect(config: Config, timeout: number = 5000): void {
  // 连接逻辑
}

在 web app 中,可选 port 默认 80。

案例研究

在 Node.js API,函数重载处理查询:

重载让 API 灵活。

在 React hook,重载自定义 hook。

这些应用展示函数类型如何使代码可靠。

高级用法:扩展函数能力

this 类型

在对象方法:

typescript 复制代码
interface Logger {
  log: (message: string) => void;
}

const consoleLogger: Logger = {
  log(this: Logger, message) {  // this 类型
    console.log(message);
  }
};

Rest 参数

typescript 复制代码
function sum(...numbers: number[]): number {
  return numbers.reduce((a, b) => a + b, 0);
}

参数解构

typescript 复制代码
function point({ x, y }: { x: number; y: number }): number {
  return x + y;
}

高级:重载与泛型结合(后文)。

风险与最佳实践

风险:

  • 类型宽松导致 bug。
  • 重载实现不覆盖所有。
  • 默认值副作用。

实践:

  • 总是指定返回。
  • 用接口参数复杂结构。
  • 测试边缘。
  • 文档签名。

遵循这些,函数更可靠。

结语:函数,类型安全的基石

通过本篇文章的详尽探讨,您已掌握函数基础,从签名到重载。这些知识将助您编写更可靠代码。实践:在项目添加类型。下一期探讨箭头函数与 this,敬请期待。若有疑问,欢迎交流。我们将继续前行。

相关推荐
码客前端17 小时前
理解 Flex 布局中的 flex:1 与 min-width: 0 问题
前端·css·css3
Komorebi゛17 小时前
【CSS】圆锥渐变流光效果边框样式实现
前端·css
CAU界编程小白17 小时前
Linux系统编程系列之动静态库
linux
济61717 小时前
linux(第十三期)--filezilla使用方法(实现ubuntu和windows11文件互传)-- Ubuntu20.04
linux·运维·ubuntu
HIT_Weston17 小时前
91、【Ubuntu】【Hugo】搭建私人博客:侧边导航栏(五)
linux·运维·ubuntu
oMcLin18 小时前
如何在 Rocky Linux 8.6 上配置并调优 Nginx 与 Lua 脚本,提升 API 网关的性能与并发处理能力
linux·nginx·lua
工藤学编程18 小时前
零基础学AI大模型之CoT思维链和ReAct推理行动
前端·人工智能·react.js
徐同保18 小时前
上传文件,在前端用 pdf.js 提取 上传的pdf文件中的图片
前端·javascript·pdf
怕浪猫18 小时前
React从入门到出门第四章 组件通讯与全局状态管理
前端·javascript·react.js