TypeScript 中的 args 详解,和 arguments 有什么不同?
点击查看原文完整示例代码
args 在 TypeScript 中通常指函数参数,特别是剩余参数(rest parameters)。让我详细解释各种用法和场景。
1. 剩余参数(Rest Parameters)
基本语法
// 使用 ...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带固定参数的剩余参数
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 对象:
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 允许对剩余参数使用元组类型:
// 固定长度和类型的剩余参数
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. 在箭头函数中的使用
// 箭头函数中的剩余参数
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:高阶函数和装饰器
// 函数日志装饰器
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:配置合并
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:事件处理器
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. 泛型与剩余参数
// 泛型剩余参数 - 保持类型安全
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" 和 57. 类型工具与 args
 
Parameters 工具类型
// 获取函数的参数类型
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:参数验证
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:函数组合
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:条件参数处理
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 剩余参数
 
// ❌ 传统方式 - 没有类型安全
function oldWay() {console.log(arguments[0], arguments[1]); // 没有类型提示
}// ✅ 现代方式 - 类型安全
function newWay(...args: [string, number]) {console.log(args[0], args[1]); // 有完整的类型提示
}注意 2:性能考虑
// 对于性能敏感的场景,避免在热路径中使用剩余参数
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 主要涉及:
- 剩余参数 (...args):收集多个参数到数组
- arguments对象:函数内可访问的类数组对象
- 元组类型的剩余参数:提供更精确的类型控制
- 泛型剩余参数:保持类型安全的可变参数
最佳实践:
- 优先使用剩余参数而非 arguments对象
- 为剩余参数提供明确的类型注解
- 在需要精确类型控制时使用元组类型
- 考虑性能影响,在热路径中避免不必要的数组创建
args 是 TypeScript 函数编程中的重要概念,合理使用可以大大增强代码的灵活性和类型安全性。
希望这些信息能帮助您。如果还有其他想了解的,随时可以留言。
