args 在 TypeScript 中通常指函数参数,特别是剩余参数(rest parameters)。让我详细解释各种用法和场景。

1. 剩余参数(Rest Parameters)

基本语法

1
2
3
4
5
6
7
8
// 使用 ...args 收集所有参数到一个数组中
function sum(...args: number[]): number {
return args.reduce((total, current) => total + current, 0);
}

console.log(sum(1, 2, 3, 4, 5)); // 15
console.log(sum(10, 20)); // 30

带固定参数的剩余参数

1
2
3
4
5
6
7
function greet(greeting: string, ...names: string[]): string {
return `${greeting}, ${names.join(', ')}!`;
}

console.log(greet("Hello", "Alice", "Bob", "Charlie"));
// "Hello, Alice, Bob, Charlie!"

2. 函数参数对象(Arguments Object)

在 TypeScript/JavaScript 中,每个函数内部都可以访问 arguments 对象:

1
2
3
4
5
6
7
8
9
10
function traditionalSum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}

console.log(traditionalSum(1, 2, 3)); // 6

注意arguments 不是数组,而是类数组对象

3. 元组类型的剩余参数

TypeScript 允许对剩余参数使用元组类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 固定长度和类型的剩余参数
function processData(name: string, ...[first, second]: [number, number]): void {
console.log(`Name: ${name}, Numbers: ${first}, ${second}`);
}

processData("John", 10, 20); // 正常
// processData("John", 10); // 错误:缺少第二个数字参数

// 混合类型的剩余参数
function mixedArgs(...args: [string, number, ...boolean[]]): void {
console.log(args);
}

mixedArgs("hello", 42, true, false, true);

4. 在箭头函数中的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 箭头函数中的剩余参数
const multiplyAll = (...numbers: number[]): number => {
return numbers.reduce((product, num) => product * num, 1);
};

console.log(multiplyAll(2, 3, 4)); // 24

// 带泛型的剩余参数
const mergeArrays = <T>(...arrays: T[][]): T[] => {
return arrays.flat();
};

const result = mergeArrays([1, 2], [3, 4], [5, 6]); // [1, 2, 3, 4, 5, 6]

5. 实际应用场景

场景 1:高阶函数和装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 函数日志装饰器
function logExecution<T extends (...args: any[]) => any>(
target: T
): (...args: Parameters<T>) => ReturnType<T> {
return function (...args: Parameters<T>): ReturnType<T> {
console.log(`函数被调用,参数:`, args);
const result = target(...args);
console.log(`函数返回:`, result);
return result;
};
}

const loggedSum = logExecution(sum);
loggedSum(1, 2, 3); // 会输出调用日志

场景 2:配置合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface Config {
timeout?: number;
retries?: number;
baseURL?: string;
}

function createApiClient(defaultConfig: Config, ...configs: Partial<Config>[]): Config {
return configs.reduce((merged, config) => ({ ...merged, ...config }), defaultConfig);
}

const client = createApiClient(
{ timeout: 5000, baseURL: "/api" },
{ retries: 3 },
{ timeout: 3000 }
);
// 结果: { timeout: 3000, baseURL: "/api", retries: 3 }

场景 3:事件处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class EventEmitter {
private events: Map<string, Function[]> = new Map();

on(event: string, ...handlers: ((...args: any[]) => void)[]): void {
if (!this.events.has(event)) {
this.events.set(event, []);
}
this.events.get(event)!.push(...handlers);
}

emit(event: string, ...args: any[]): void {
const handlers = this.events.get(event) || [];
handlers.forEach(handler => handler(...args));
}
}

const emitter = new EventEmitter();
emitter.on("message", (msg: string, priority: number) => {
console.log(`消息: ${msg}, 优先级: ${priority}`);
});

emitter.emit("message", "Hello World", 1);

6. 泛型与剩余参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 泛型剩余参数 - 保持类型安全
function callAll<T extends any[]>(
...functions: ((...args: T) => void)[]
): (...args: T) => void {
return (...args: T) => {
functions.forEach(fn => fn(...args));
};
}

// 使用
const logger = (msg: string, count: number) => console.log(msg, count);
const storage = (msg: string, count: number) => localStorage.setItem('last', msg);

const combined = callAll(logger, storage);
combined("test", 5); // 两个函数都会接收到 "test" 和 5

7. 类型工具与 args

Parameters 工具类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 获取函数的参数类型
type SumParams = Parameters<typeof sum>; // [number[]]
type GreetParams = Parameters<typeof greet>; // [string, ...string[]]

// 实现一个包装函数
function withRetry<T extends any[], R>(
fn: (...args: T) => R,
retries: number
): (...args: T) => R {
return function (...args: T): R {
let lastError: Error;

for (let i = 0; i < retries; i++) {
try {
return fn(...args);
} catch (error) {
lastError = error as Error;
}
}

throw lastError!;
};
}

8. 常见模式和最佳实践

模式 1:参数验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function validateAndProcess(...inputs: (string | number)[]): void {
if (inputs.length === 0) {
throw new Error("至少需要一个参数");
}

inputs.forEach((input, index) => {
if (input === null || input === undefined) {
throw new Error(`参数 ${index} 不能为 null 或 undefined`);
}
});

// 处理逻辑...
}

模式 2:函数组合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function compose<T extends any[]>(
...functions: ((...args: T) => void)[]
): (...args: T) => void {
return (...args: T) => {
functions.forEach(fn => {
try {
fn(...args);
} catch (error) {
console.error('函数执行错误:', error);
}
});
};
}

模式 3:条件参数处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function flexibleProcessor(
required: string,
...optional: (number | string)[]
): { required: string; optional: (number | string)[] } {

const processedOptional = optional.map(arg => {
if (typeof arg === 'string') {
return arg.toUpperCase();
}
return arg * 2;
});

return { required, optional: processedOptional };
}

9. 注意事项

注意 1:arguments vs 剩余参数

1
2
3
4
5
6
7
8
9
10
// ❌ 传统方式 - 没有类型安全
function oldWay() {
console.log(arguments[0], arguments[1]); // 没有类型提示
}

// ✅ 现代方式 - 类型安全
function newWay(...args: [string, number]) {
console.log(args[0], args[1]); // 有完整的类型提示
}

注意 2:性能考虑

1
2
3
4
5
6
7
8
9
10
// 对于性能敏感的场景,避免在热路径中使用剩余参数
function optimizedSum(a: number, b: number, c?: number, d?: number): number {
let total = a + b;
if (c !== undefined) total += c;
if (d !== undefined) total += d;
return total;
}

// 比使用 ...args: number[] 更高效

💎 总结

在 TypeScript 中,args 主要涉及:

  1. 剩余参数 (...args):收集多个参数到数组
  2. arguments 对象:函数内可访问的类数组对象
  3. 元组类型的剩余参数:提供更精确的类型控制
  4. 泛型剩余参数:保持类型安全的可变参数

最佳实践

  • 优先使用剩余参数而非 arguments 对象
  • 为剩余参数提供明确的类型注解
  • 在需要精确类型控制时使用元组类型
  • 考虑性能影响,在热路径中避免不必要的数组创建

args 是 TypeScript 函数编程中的重要概念,合理使用可以大大增强代码的灵活性和类型安全性。