第 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 22.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 收窄联合类型 |
动手练习
- 基础函数:写一个
formatDate(date: Date, format?: string): string函数 - 重载:实现一个
getValue(obj, key),根据 key 自动推断返回类型 - 类型守卫:写一个
isNonNullArray<T>(value: T | null): value is T[]工具函数
推荐阅读
- 📖 TypeScript Handbook - Functions — 函数完整文档
- 📖 TypeScript Handbook - More on Functions — 深入函数特性
- 🌐 TypeScript Function Overloads — 重载详解
下一章:第 98 章:接口(Interface) →