mcpField
A
avoid-repeated-params

avoid-repeated-params

github.com/marius-townhouse开发642 次安装38 周前更新
代码仓库

关于 avoid-repeated-params

这项 TypeScript 技能帮助设计不易误用的函数签名,尤其适用于多个参数类型相同的情况。它建议在位置参数容易混淆时改用带名称的对象参数。

主要功能

  1. 01易混淆参数改用对象传递
  2. 02让调用代码自带说明
  3. 03按含义组合相关值
  4. 04清晰的惯例场景保留位置参数

适用场景

  1. 01设计含多个字符串参数的 API
  2. 02明确日期格式化参数含义
  3. 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