TypeScript编程04-函数
文章目录
- TypeScript编程04-函数
-
- [一、箭头函数(Arrow Functions)](#一、箭头函数(Arrow Functions))
-
- [1.1 基本语法与特性](#1.1 基本语法与特性)
- [1.2 箭头函数的 `this` 绑定](#1.2 箭头函数的
this绑定) - [1.3 箭头函数的类型推断](#1.3 箭头函数的类型推断)
- [二、参数函数(Function Parameters)](#二、参数函数(Function Parameters))
-
- [2.1 可选参数与默认参数](#2.1 可选参数与默认参数)
- [2.2 剩余参数(Rest Parameters)](#2.2 剩余参数(Rest Parameters))
- [2.3 函数重载(Function Overloads)](#2.3 函数重载(Function Overloads))
- [2.4 参数解构与命名参数](#2.4 参数解构与命名参数)
- 三、高级应用与常见问题
-
- [3.1 回调函数与类型定义](#3.1 回调函数与类型定义)
- [3.2 泛型函数](#3.2 泛型函数)
- [3.3 常见问题排查](#3.3 常见问题排查)
- 四、最佳实践总结
-
- [4.1 箭头函数使用建议](#4.1 箭头函数使用建议)
- [4.2 参数设计原则](#4.2 参数设计原则)
- [4.3 类型声明建议](#4.3 类型声明建议)
一、箭头函数(Arrow Functions)
1.1 基本语法与特性
Q1:TypeScript 箭头函数的基本语法是什么?
typescript
// 基本语法
const 函数名 = (参数1: 类型, 参数2: 类型): 返回值类型 => {
// 函数体
return 结果;
};
// 单行简写(省略 return 和花括号)
const add = (a: number, b: number): number => a + b;
// 无参数
const greet = (): string => "Hello TypeScript";
// 单个参数可省略括号
const square = (x: number): number => x * x;
Q2:箭头函数与普通函数的主要区别是什么?
| 特性 | 箭头函数 | 普通函数 |
|---|---|---|
this 绑定 |
词法作用域(继承外层) | 动态作用域(调用时确定) |
arguments 对象 |
没有自己的 arguments |
有自己的 arguments |
| 构造函数 | 不能作为构造函数使用 | 可以作为构造函数 |
| 原型属性 | 没有 prototype |
有 prototype |
| 适用场景 | 回调函数、方法简写 | 需要动态 this 的场景 |
1.2 箭头函数的 this 绑定
Q3:为什么在类方法中使用箭头函数可以避免 this 丢失问题?
typescript
class Counter {
count: number = 0;
// 箭头函数:this 绑定到定义时的上下文(类实例)
increment = (): void => {
this.count++; // 这里的 this 始终指向 Counter 实例
};
// 普通函数:this 取决于调用方式
decrement(): void {
this.count--;
}
}
const counter = new Counter();
// 作为回调传递时
setTimeout(counter.increment, 100); // ✅ 正常工作,this 正确
setTimeout(counter.decrement, 100); // ❌ this 可能丢失,变成 undefined 或 window
Q4:如何在 TypeScript 中正确为类方法绑定 this?
typescript
class Button {
text: string = "Click me";
// 方案1:箭头函数属性(推荐用于回调)
handleClick = (): void => {
console.log(this.text);
};
// 方案2:构造函数中绑定
constructor() {
this.handleClick2 = this.handleClick2.bind(this);
}
handleClick2(): void {
console.log(this.text);
}
}
1.3 箭头函数的类型推断
Q5:TypeScript 如何推断箭头函数的返回类型?
typescript
// 显式声明返回类型
const multiply = (a: number, b: number): number => a * b;
// 类型推断:TypeScript 自动推断返回类型
const divide = (a: number, b: number) => a / b; // 推断为 number
// 复杂返回类型建议显式声明
const getUser = (): { id: number; name: string } => ({
id: 1,
name: "张三"
});
Q6:如何为箭头函数定义类型别名?
typescript
// 定义函数类型别名
type MathOperation = (a: number, b: number) => number;
// 使用类型别名
const add: MathOperation = (x, y) => x + y;
const subtract: MathOperation = (x, y) => x - y;
// 带泛型的函数类型
type Transform<T, R> = (input: T) => R;
const toString: Transform<number, string> = (num) => num.toString();
二、参数函数(Function Parameters)
2.1 可选参数与默认参数
Q7:TypeScript 中如何定义可选参数和默认参数?
typescript
// 可选参数(使用 ? 标记)
function greet(name: string, greeting?: string): string {
if (greeting) {
return `${greeting}, ${name}!`;
}
return `Hello, ${name}!`;
}
// 默认参数
function createUser(
name: string,
age: number = 18,
isActive: boolean = true
) {
return { name, age, isActive };
}
// 调用示例
createUser("张三"); // { name: "张三", age: 18, isActive: true }
createUser("李四", 25); // { name: "李四", age: 25, isActive: true }
createUser("王五", 30, false); // { name: "王五", age: 30, isActive: false }
Q8:可选参数和默认参数的位置规则是什么?
typescript
// ✅ 正确:可选参数在必选参数之后
function func1(a: number, b?: number, c?: number) {}
// ✅ 正确:默认参数可以在任意位置(但建议放在后面)
function func2(a: number, b: number = 10, c?: number) {}
// ❌ 错误:可选参数不能在必选参数前面
// function func3(a?: number, b: number) {} // Error!
// 解决方案:使用默认参数 undefined
function func4(a: number | undefined, b: number) {}
2.2 剩余参数(Rest Parameters)
Q9:如何在 TypeScript 中使用剩余参数?
typescript
// 剩余参数必须是数组类型
function sum(...numbers: number[]): number {
return numbers.reduce((total, num) => total + num, 0);
}
// 与其他参数结合使用
function buildName(firstName: string, ...restNames: string[]): string {
return [firstName, ...restNames].join(" ");
}
// 调用
sum(1, 2, 3, 4, 5); // 15
buildName("张", "三", "丰"); // "张 三 丰"
Q10:剩余参数的类型推断与约束
typescript
// 元组类型约束剩余参数
function tupleFunc(...args: [string, number, boolean]): void {
const [name, age, isActive] = args;
console.log(name, age, isActive);
}
// 调用必须严格匹配元组结构
tupleFunc("张三", 25, true); // ✅
// tupleFunc("张三", 25); // ❌ 参数不足
// tupleFunc("张三", 25, true, "extra"); // ❌ 参数过多
2.3 函数重载(Function Overloads)
Q11:TypeScript 函数重载的基本用法是什么?
typescript
// 重载签名(Overload Signatures)
function processInput(input: string): string;
function processInput(input: number): number;
function processInput(input: boolean): boolean;
// 实现签名(Implementation Signature)
function processInput(input: string | number | boolean): string | number | boolean {
if (typeof input === "string") {
return input.toUpperCase();
} else if (typeof input === "number") {
return input * 2;
} else {
return !input;
}
}
// 调用
const r1 = processInput("hello"); // 类型推断为 string
const r2 = processInput(10); // 类型推断为 number
const r3 = processInput(true); // 类型推断为 boolean
Q12:箭头函数如何实现函数重载?
typescript
// 箭头函数不能直接重载,需要通过类型别名实现
type StringProcessor = {
(input: string): string;
(input: string[]): string[];
};
const processString: StringProcessor = (input: string | string[]) => {
if (typeof input === "string") {
return input.toUpperCase();
}
return input.map(s => s.toUpperCase());
};
// 或者使用接口
interface NumberProcessor {
(input: number): number;
(input: number[]): number[];
}
const processNumber: NumberProcessor = (input) => {
if (Array.isArray(input)) {
return input.map(n => n * 2);
}
return input * 2;
};
2.4 参数解构与命名参数
Q13:如何在函数参数中使用解构赋值?
typescript
// 对象解构
function createUser({ name, age, email }: {
name: string;
age: number;
email: string;
}): void {
console.log(`用户:${name}, 年龄:${age}, 邮箱:${email}`);
}
// 带默认值的对象解构
function printConfig({
host = "localhost",
port = 3000,
debug = false
}: {
host?: string;
port?: number;
debug?: boolean;
}): void {
console.log(`${host}:${port}, 调试模式:${debug}`);
}
// 数组解构
function processPoint([x, y]: [number, number]): number {
return Math.sqrt(x ** 2 + y ** 2);
}
Q14:如何实现命名参数模式(Named Parameters)?
typescript
// TypeScript 没有原生命名参数,通过对象模拟
interface FetchOptions {
url: string;
method?: "GET" | "POST" | "PUT" | "DELETE";
headers?: Record<string, string>;
timeout?: number;
}
function fetchData(options: FetchOptions): Promise<any> {
const { url, method = "GET", headers = {}, timeout = 5000 } = options;
// 实现逻辑...
return fetch(url, { method, headers, signal: AbortSignal.timeout(timeout) });
}
// 调用清晰明了,类似命名参数
fetchData({
url: "/api/users",
method: "POST",
timeout: 10000
});
三、高级应用与常见问题
3.1 回调函数与类型定义
Q15:如何正确定义回调函数的类型?
typescript
// 定义回调函数类型
type Callback<T> = (result: T, error?: Error) => void;
function fetchUser(id: number, callback: Callback<{ name: string }>): void {
// 模拟异步操作
setTimeout(() => {
if (id > 0) {
callback({ name: "张三" }, undefined);
} else {
callback(undefined as any, new Error("Invalid ID"));
}
}, 1000);
}
// 使用
fetchUser(1, (user, err) => {
if (err) {
console.error(err);
} else {
console.log(user.name);
}
});
3.2 泛型函数
Q16:如何创建泛型箭头函数?
typescript
// 泛型箭头函数
const identity = <T>(arg: T): T => arg;
// 多个泛型参数
const mapPair = <T, U>(a: T, b: U): [T, U] => [a, b];
// 泛型约束
interface HasLength {
length: number;
}
const logLength = <T extends HasLength>(arg: T): T => {
console.log(arg.length);
return arg;
};
// 调用
logLength("hello"); // ✅ string 有 length
logLength([1, 2, 3]); // ✅ 数组有 length
// logLength(123); // ❌ number 没有 length
3.3 常见问题排查
Q17:遇到 "Parameter 'x' implicitly has an 'any' type" 错误如何解决?
typescript
// ❌ 错误:隐式 any
const badFunc = (x) => x + 1;
// ✅ 解决方案1:显式声明类型
const goodFunc1 = (x: number): number => x + 1;
// ✅ 解决方案2:开启上下文类型推断(在回调中常见)
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2); // TypeScript 自动推断 num 为 number
Q18:如何处理 this 参数的类型声明?
typescript
interface User {
name: string;
age: number;
}
function greetUser(this: User, greeting: string): string {
return `${greeting}, ${this.name}!`;
}
// 使用
const user: User = { name: "张三", age: 25 };
const boundGreet = greetUser.bind(user);
boundGreet("你好"); // "你好, 张三!"
// 直接调用会报错
// greetUser("你好"); // Error: 'this' 上下文类型不匹配
四、最佳实践总结
4.1 箭头函数使用建议
| 场景 | 推荐方案 |
|---|---|
| 简单的回调函数 | 使用箭头函数,利用类型推断 |
| 类中的事件处理 | 使用箭头函数属性避免 this 绑定问题 |
需要 arguments 对象 |
使用普通函数或剩余参数替代 |
需要动态 this |
使用普通函数 |
4.2 参数设计原则
- 必选参数在前,可选参数在后
- 默认参数值放在参数列表末尾
- 参数过多时(>3个)使用对象解构模拟命名参数
- 复杂函数使用重载提高类型安全性
4.3 类型声明建议
typescript
// 优先使用类型别名定义函数类型
type Handler<T> = (event: T) => void;
// 复杂函数结构使用接口
interface EventHandlers {
onClick: (event: MouseEvent) => void;
onChange: (value: string) => void;
}
// 泛型函数保持简洁,约束条件明确
const pipe = <T, R>(...fns: Array<(arg: T) => T>): ((x: T) => T) =>
fns.reduce((f, g) => (x) => g(f(x)));