Skip to content
第 97 / 250 章前端⏱ 10 分钟阅读

第 97 章:函数类型

学习目标

  • 掌握函数类型的多种定义方式
  • 理解函数参数的各种修饰(可选/默认/剩余)
  • 学会 this 类型的标注
  • 掌握函数重载与类型谓词

一、函数类型定义

1.1 函数声明

typescript
function add(a: number, b: number): number {
  return a + b;
}

1.2 函数表达式

typescript
const add = (a: number, b: number): number => a + b;

1.3 完整类型标注

typescript
// 语法:变量: (参数列表) => 返回类型
const add: (a: number, b: number) => number = (a, b) => a + b;

1.4 提取为类型别名

typescript
type BinaryOp = (a: number, b: number) => number;

const add: BinaryOp = (a, b) => a + b;
const sub: BinaryOp = (a, b) => a - b;
const mul: BinaryOp = (a, b) => a * b;

二、参数类型

2.1 必选参数

typescript
function greet(name: string): string {
  return `Hello, ${name}`;
}

greet('Tom');     // ✅
greet();          // ❌ Expected 1 argument, but got 0
greet('Tom', 18); // ❌ Expected 1 argument, but got 2

2.2 可选参数

typescript
function greet(name: string, greeting?: string): string {
  return `${greeting ?? 'Hello'}, ${name}`;
}

greet('Tom');              // ✅ "Hello, Tom"
greet('Tom', 'Hi');        // ✅ "Hi, Tom"
greet('Tom', undefined);   // ✅ 显式 undefined

注意:可选参数必须放在必选参数后面。

2.3 默认参数

typescript
function greet(name: string, greeting: string = 'Hello'): string {
  return `${greeting}, ${name}`;
}

greet('Tom');              // ✅ "Hello, Tom"
greet('Tom', 'Hi');        // ✅ "Hi, Tom"

2.4 剩余参数(Rest)

typescript
function sum(...nums: number[]): number {
  return nums.reduce((a, b) => a + b, 0);
}

sum(1, 2);            // 3
sum(1, 2, 3, 4, 5);   // 15
sum();                // 0

// 剩余参数 + 必选参数
function greet(greeting: string, ...names: string[]): string {
  return `${greeting}, ${names.join(', ')}`;
}

greet('Hi', 'Tom');              // "Hi, Tom"
greet('Hi', 'Tom', 'Jerry');      // "Hi, Tom, Jerry"

三、返回类型

3.1 显式返回类型

typescript
function add(a: number, b: number): number {
  return a + b;
}

3.2 类型推断(推荐)

typescript
function add(a: number, b: number) {
  return a + b;  // 推断返回 number
}

3.3 void 返回

typescript
function log(message: string): void {
  console.log(message);
}

let result: void = log('hello');  // undefined 是 void 的子类型

3.4 never 返回(永不返回)

typescript
function throwError(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {}
}

3.5 异步函数

typescript
async function fetchUser(id: number): Promise<User> {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
}

// ❌ 错误:忘了 await
function fetchUser(id: number): Promise<User> {
  const res = fetch(`/api/users/${id}`);  // ❌ Promise<Promise<User>>
  return res.json();
}

四、this 类型

4.1 this 的隐式类型

typescript
const user = {
  name: 'Tom',
  greet() {
    console.log(`Hello, ${this.name}`);  // ✅ this 推断为 { name: string; greet(): void }
  }
};

4.2 独立函数中的 this

typescript
// ❌ this 隐式为 any
function greet() {
  console.log(this.name);  // ❌ 'this' implicitly has type 'any'
}

// ✅ 显式标注
function greet(this: User) {
  console.log(`Hello, ${this.name}`);
}

4.3 显式 this 参数

typescript
interface User {
  name: string;
  greet(this: User): string;
}

const u: User = {
  name: 'Tom',
  greet() {
    return `Hello, ${this.name}`;
  }
};

五、函数类型 vs 调用签名

5.1 普通函数类型

typescript
type Add = (a: number, b: number) => number;
const add: Add = (a, b) => a + b;

5.2 调用签名(Call Signature)

typescript
// 对象可以"调用"自己
type DescribableFunction = {
  description: string;
  (a: number, b: number): number;
};

const add: DescribableFunction = Object.assign(
  (a: number, b: number) => a + b,
  { description: '加法函数' }
);

add(1, 2);            // ✅ 调用
add.description;      // ✅ "加法函数"

5.3 构造签名(Constructor Signature)

typescript
// 类可以用 new 创建
type UserConstructor = {
  new (name: string): User;
};

function createUser(ctor: UserConstructor, name: string): User {
  return new ctor(name);
}

六、函数重载(Overload)

6.1 为什么需要重载

typescript
// ❌ 一个函数无法同时支持 string 和 number 的不同处理
function getInfo(value: string): string;
function getInfo(value: number): number;
function getInfo(value: string | number): string | number {
  if (typeof value === 'string') {
    return value.length;  // 应该是 number
  }
  return value.toFixed(2);  // 应该是 string
}

6.2 正确写法

typescript
// ① 重载签名(多个)
function getInfo(value: string): string;
function getInfo(value: number): number;

// ② 实现签名(必须兼容所有,参数一般是宽类型)
function getInfo(value: string | number): string | number {
  if (typeof value === 'string') {
    return value.toUpperCase();   // ✅ 返回 string
  }
  return value.toFixed(2);        // ✅ 返回 string
}

// 调用:TS 自动匹配签名
getInfo('hello');  // ✅ 返回 string
getInfo(123);      // ❌ 实现签名返回 string|number,与重载签名不匹配

6.3 实战案例

typescript
// 数组去重:支持数字/字符串
function unique(arr: number[]): number[];
function unique(arr: string[]): string[];
function unique(arr: (number | string)[]): (number | string)[] {
  return [...new Set(arr)];
}

const nums = unique([1, 2, 2, 3]);    // ✅ 类型 number[]
const strs = unique(['a', 'b', 'a']); // ✅ 类型 string[]

七、参数解构的类型

typescript
function greet({ name, age }: { name: string; age: number }): string {
  return `${name} (${age})`;
}

// 提取为类型
type User = { name: string; age: number };
function greet({ name, age }: User): string {
  return `${name} (${age})`;
}

八、类型谓词(Type Predicates)

类型守卫函数的返回值类型:

typescript
// 基础函数
function isString(value: unknown): boolean {
  return typeof value === 'string';
}

// ✅ 类型谓词:返回类型是 `param is Type`
function isString2(value: unknown): value is string {
  return typeof value === 'string';
}

// 使用
function process(value: string | number) {
  if (isString2(value)) {
    value.toUpperCase();  // ✅ 收窄为 string
  }
}

// 复杂类型谓词
interface Cat { meow(): void }
interface Dog { bark(): void }

function isCat(animal: Cat | Dog): animal is Cat {
  return (animal as Cat).meow !== undefined;
}

九、常见错误

9.1 可选参数位置错误

typescript
// ❌ 可选参数必须在必选参数之后
function greet(name?: string, greeting: string): string { ... }

// ✅ 正确
function greet(greeting: string, name?: string): string { ... }

9.2 void 和 undefined

typescript
// ❌ 期望返回 number 但写了 return;
function add(a: number, b: number): number {
  if (a < 0) return;  // ❌ Type 'undefined' is not assignable to type 'number'
  return a + b;
}

9.3 参数数量不匹配

typescript
function add(a: number, b: number): number { return a + b; }
add(1, 2, 3);  // ❌ Expected 2 arguments, but got 3

十、实战:完整的函数库

typescript
// types.ts
export type Predicate<T> = (item: T) => boolean;
export type Mapper<T, U> = (item: T) => U;
export type Reducer<T, U> = (acc: U, item: T) => U;

// array-utils.ts
export function filter<T>(arr: T[], predicate: Predicate<T>): T[] {
  return arr.filter(predicate);
}

export function map<T, U>(arr: T[], mapper: Mapper<T, U>): U[] {
  return arr.map(mapper);
}

export function reduce<T, U>(arr: T[], reducer: Reducer<T, U>, initial: U): U {
  return arr.reduce(reducer, initial);
}

// 使用
const nums = [1, 2, 3, 4, 5];
const evens = filter(nums, n => n % 2 === 0);     // number[]
const doubled = map(nums, n => n * 2);            // number[]
const sum = reduce(nums, (a, b) => a + b, 0);     // number

十一、本章小结

要点关键
函数类型(a: number, b: number) => number
参数必选/可选/默认/剩余
返回类型显式标注或类型推断;void / never / Promise
this独立函数或回调需要显式标注
函数重载多个签名 + 一个实现签名
类型谓词param is Type 收窄联合类型

动手练习

  1. 基础函数:写一个 formatDate(date: Date, format?: string): string 函数
  2. 重载:实现一个 getValue(obj, key),根据 key 自动推断返回类型
  3. 类型守卫:写一个 isNonNullArray<T>(value: T | null): value is T[] 工具函数

推荐阅读


下一章第 98 章:接口(Interface)

本站基于 VitePress 构建 · 由 Codebook 团队维护