关于 avoid-repeated-params
这项 TypeScript 技能帮助设计不易误用的函数签名,尤其适用于多个参数类型相同的情况。它建议在位置参数容易混淆时改用带名称的对象参数。
主要功能
- 01易混淆参数改用对象传递
- 02让调用代码自带说明
- 03按含义组合相关值
- 04清晰的惯例场景保留位置参数
适用场景
- 01设计含多个字符串参数的 API
- 02明确日期格式化参数含义
- 03组合矩形的位置与尺寸
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill avoid-repeated-params安装后在 Claude Code 中输入/avoid-repeated-params确认已加载。
详细介绍
避免重复使用相同类型的参数
概述
多个相同类型的参数容易造成混淆。
当函数接受 (string, string, string) 时,很容易按错误的顺序传入参数。使用命名参数(对象),让调用过程一目了然。
何时使用此技巧
- 有 2 个或更多相同类型参数的函数
- 容易混淆的参数
- 用户会频繁调用的 API
- 重构令人困惑的函数签名
铁律
If parameter order is confusing, use an object.
Names prevent mix-ups better than positions.
请记住:
- TypeScript 无法检测相同类型的参数是否被调换
- 基于位置的 API 需要先研读文档
- 对象参数一目了然
- IDE 自动补全对命名参数的支持更好
检测:容易混淆
function sendEmail(
to: string,
subject: string,
body: string
) { /* ... */ }
// Which is which?
sendEmail(
'Hello!',
'[email protected]',
'Welcome to our service'
);
// Whoops! Subject and recipient are swapped
TypeScript 看到的是 (string, string, string)——全部有效,不会报错。
解决方案:对象参数
function sendEmail(params: {
to: string;
subject: string;
body: string;
}) { /* ... */ }
// Now it's clear:
sendEmail({
to: '[email protected]',
subject: 'Hello!',
body: 'Welcome to our service'
});
// Wrong order is obvious:
sendEmail({
to: 'Hello!', // Clearly wrong
subject: '[email protected]', // Obviously an email
body: 'Welcome'
});
何时可以使用基于位置的参数
类型不同
// Clear: types are different
function setProperty(obj: object, key: string, value: unknown) { }
// Clear: number vs string
function pad(str: string, length: number) { }
单个参数
// Nothing to confuse
function greet(name: string) { }
广为人知的约定
// Math functions are universally position-based
function max(a: number, b: number) { }
// Array methods follow established patterns
function slice(arr: any[], start: number, end: number) { }
使用解构实现简洁的代码
// Clean function signature and implementation
function sendEmail({
to,
subject,
body
}: {
to: string;
subject: string;
body: string;
}) {
console.log(`Sending to ${to}: ${subject}`);
// Use to, subject, body directly
}
带默认值的可选参数
interface SendEmailOptions {
to: string;
subject: string;
body: string;
cc?: string[];
priority?: 'high' | 'normal' | 'low';
}
function sendEmail({
to,
subject,
body,
cc = [],
priority = 'normal'
}: SendEmailOptions) {
// ...
}
// Call with just required params
sendEmail({ to: '[email protected]', subject: 'Hi', body: 'Hello!' });
// Or with optional params
sendEmail({
to: '[email protected]',
subject: 'Urgent',
body: 'Please respond',
priority: 'high'
});
混合必填和可选参数
// Required first parameter, options object second
function createElement(
tagName: string,
options?: {
className?: string;
id?: string;
children?: Element[];
}
) { /* ... */ }
createElement('div');
createElement('div', { className: 'container', id: 'main' });
真实示例:日期格式化
// Bad: which is format, which is locale?
function formatDate(date: Date, format: string, locale: string): string { }
formatDate(new Date(), 'en-US', 'YYYY-MM-DD'); // Wrong order!
// Good: named parameters
function formatDate(
date: Date,
options: { format: string; locale: string }
): string { }
formatDate(new Date(), { format: 'YYYY-MM-DD', locale: 'en-US' });
矩形示例
// Bad: easy to confuse width/height, x/y
function drawRect(x: number, y: number, width: number, height: number) { }
// Better: grouped semantically
function drawRect(
position: { x: number; y: number },
size: { width: number; height: number }
) { }
// Best: named everything
interface DrawRectOptions {
x: number;
y: number;
width: number;
height: number;
}
function drawRect(options: DrawRectOptions) { }
抵御压力的原则
1. “太冗长了”
压力:“对象语法更长”
**回应:**一次性的冗长可以避免持续的混淆。
**行动:**使用命名参数来提高清晰度;简短不值得以引入缺陷为代价。
2. “这是标准模式”
压力:“其他库都使用基于位置的参数”
**回应:**你不是其他库。让你的 API 清晰易懂。
**行动:**为用户设计,而不是拘泥于惯例。
警示信号——请停下来重新考虑
- 有 2 个或更多相同类型参数的函数
- 需要通过文档解释参数顺序
- 验证参数顺序的测试
- 因参数调换而产生的缺陷
常见的自我辩解(均不成立)
| 借口 | 事实 |
|---|---|
| “只有两个参数” | 两个字符串仍然容易混淆 |
| “IDE 会显示参数名称” | 只会显示名称,不会强制要求 |
| “我们的文档写得很好” | 人们不会读文档 |
快速参考
// DON'T: Same-type positional parameters
function fn(a: string, b: string, c: string) { }
fn('x', 'y', 'z'); // What's what?
// DO: Named parameters
function fn(params: { a: string; b: string; c: string }) { }
fn({ a: 'x', b: 'y', c: 'z' }); // Clear!
// OK: Different types
function fn(name: string, count: number) { }
// OK: Single parameter
function fn(message: string) { }
归根结底
命名参数可以避免传错参数。
当函数接受多个相同类型的参数时,请使用对象参数。虽然会略微增加冗长度,但能避免缺陷并提升可读性,长期来看收益远超成本。
参考资料
改编自 Dan Vanderkam 的《Effective TypeScript》,第 38 条:避免重复使用相同类型的参数。
评论
支持 Markdown