Skip to main content

no-unnecessary-type-parameters

Disallow type parameters that aren't used multiple times.

🔒

ESLint 配置 中扩展"plugin:@typescript-eslint/strict-type-checked" 可启用此规则。

💡

此规则报告的一些问题可以通过编辑器 建议 手动修复。

💭

该规则需要 类型信息 才能运行,但这会带来性能方面的权衡。

此规则禁止在函数、方法或类定义中未多次使用的类型参数。

¥This rule forbids type parameters that aren't used multiple times in a function, method, or class definition.

类型参数与两种类型相关。如果类型参数仅使用一次,则它与任何内容都无关。它通常可以用显式类型(如 unknown)替换。

¥Type parameters relate two types. If a type parameter is only used once, then it is not relating anything. It can usually be replaced with explicit types such as unknown.

在最好的情况下,不必要的类型参数会使代码更难阅读。在最坏的情况下,它们可用于伪装不安全的类型断言。

¥At best unnecessary type parameters make code harder to read. At worst they can be used to disguise unsafe type assertions.

警告

此规则是最近添加的,与我们的大多数规则相比,它具有令人惊讶的隐藏复杂性。如果你遇到意外行为,请仔细检查下面的 局限性常见问题 部分以及我们的 问题跟踪器。如果你认为你的案例没有被覆盖,请使用 联系我们

¥This rule was recently added, and has a surprising amount of hidden complexity compared to most of our rules. If you encounter unexpected behavior with it, please check closely the Limitations and FAQ sections below and our issue tracker. If you don't see your case covered, please reach out to us!

eslint.config.mjs
export default tseslint.config({
rules: {
"@typescript-eslint/no-unnecessary-type-parameters": "error"
}
});

在线运行试试这个规则 ↗

示例

¥Examples

function second<A, B>(a: A, b: B): B {
return b;
}

function parseJSON<T>(input: string): T {
return JSON.parse(input);
}

function printProperty<T, K extends keyof T>(obj: T, key: K) {
console.log(obj[key]);
}
Open in Playground

局限性

¥Limitations

请注意,此规则允许多次使用任何类型参数,即使这些使用是通过类型参数进行的。例如,以下 T 由于位于 Array 中而被多次使用,即使其名称在声明后仅出现一次:

¥Note that this rule allows any type parameter that is used multiple times, even if those uses are via a type argument. For example, the following T is used multiple times by virtue of being in an Array, even though its name only appears once after declaration:

declare function createStateHistory<T>(): T[];

这是因为类型参数 TT[]Array<T>)中的多个方法关联在一起,使其多次使用。

¥This is because the type parameter T relates multiple methods in T[] (Array<T>) together, making it used more than once.

因此,此规则不会报告用作类型参数的类型参数。这包括提供给全局类型(例如 ArrayMapSet)的类型参数,这些类型参数具有多个方法和属性,可以根据类型参数更改值。

¥Therefore, this rule won't report on type parameters used as a type argument. This includes type arguments provided to global types such as Array, Map, and Set that have multiple methods and properties that can change values based on the type parameter.

另一方面,只读和固定数组类(例如 readonly T[]ReadonlyArray)和元组(例如 [T])是特殊情况,在用作输入类型或 readonly 输出类型时会特别报告。由于 T 仅用作 ReadonlyArray 全局类型的类型参数一次,因此将报告以下示例:

¥On the other hand, readonly and fixed array-likes such as readonly T[], ReadonlyArray, and tuples such as [T] are special cases that are specifically reported on when used as input types, or as readonly output types. The following example will be reported because T is used only once as type argument for the ReadonlyArray global type:

declare function length<T>(array: ReadonlyArray<T>): number;
Open in Playground

常见问题

¥FAQ

返回类型仅用作输入,那么为什么规则不报告?

¥The return type is only used as an input, so why isn't the rule reporting?

出现这种情况的一个常见原因是未明确指定返回类型。规则使用类型信息来计算函数签名中类型参数的隐式用法,包括在推断的返回类型中。例如,以下函数...

¥One common reason that this might be the case is when the return type is not specified explicitly. The rule uses uses type information to count implicit usages of the type parameter in the function signature, including in the inferred return type. For example, the following function...

function identity<T>(arg: T) {
return arg;
}

...隐式地具有返回类型 T。因此,类型参数 T 被使用了两次,规则不会报告此功能。

¥...implicitly has a return type of T. Therefore, the type parameter T is used twice, and the rule will not report this function.

由于规则可能未报告的其他原因,请务必查看 限制部分 和其他常见问题解答。

¥For other reasons the rule might not be reporting, be sure to check the Limitations section and other FAQs.

我在函数内部使用类型参数,那么规则为什么会报告?

¥I'm using the type parameter inside the function, so why is the rule reporting?

你可能会惊讶地发现规则会报告这样的功能:

¥You might be surprised to that the rule reports on a function like this:

function log<T extends string>(string1: T): void {
const string2: T = string1;
console.log(string2);
}

毕竟,类型参数 T 将输入 string1 和局部变量 string2 关联起来,对吧?但是,这种用法是不必要的,因为我们可以通过用其约束替换类型参数的所有用法来实现相同的结果。也就是说,该函数总是可以重写为:

¥After all, the type parameter T relates the input string1 and the local variable string2, right? However, this usage is unnecessary, since we can achieve the same results by replacing all usages of the type parameter with its constraint. That is to say, the function can always be rewritten as:

function log(string1: string): void {
const string2: string = string1;
console.log(string2);
}

因此,此规则仅计算函数、方法或类的签名中类型参数的使用次数,但不计算实现中的类型参数的使用次数。另请参阅 #9735

¥Therefore, this rule only counts usages of a type parameter in the signature of a function, method, or class, but not in the implementation. See also #9735

为什么在删除不必要的类型参数后会收到 TypeScript 错误提示 "对象文字只能指定已知属性"?

¥Why am I getting TypeScript errors saying "Object literal may only specify known properties" after removing an unnecessary type parameter?

假设你遇到以下情况,这将触发规则报告。

¥Suppose you have a situation like the following, which will trigger the rule to report.

interface SomeProperties {
foo: string;
}

// T is only used once, so the rule will report.
function serialize<T extends SomeProperties>(x: T): string {
return JSON.stringify(x);
}

serialize({ foo: 'bar', anotherProperty: 'baz' });

如果我们删除不必要的类型参数,我们将收到错误:

¥If we remove the unnecessary type parameter, we'll get an error:

function serialize(x: SomeProperties): string {
return JSON.stringify(x);
}

// TS Error: Object literal may only specify known properties, and 'anotherProperty' does not exist in type 'SomeProperties'.
serialize({ foo: 'bar', anotherProperty: 'baz' });

这是因为 TypeScript 认为在需要特定类型的位置明确提供多余的属性通常是一个错误。请参阅 TypeScript 手册中关于多余属性检查的部分 以进行进一步讨论。

¥This is because TypeScript figures it's usually an error to explicitly provide excess properties in a location that expects a specific type. See the TypeScript handbook's section on excess property checks for further discussion.

要解决这个问题,你有两种方法可供选择。

¥To resolve this, you have two approaches to choose from.

  1. 如果在你的函数中接受多余的属性没有意义,你将需要修复调用站点的错误。通常,你可以简单地删除调用函数的任何多余属性。

    ¥If it doesn't make sense to accept excess properties in your function, you'll want to fix the errors at the call sites. Usually, you can simply remove any excess properties where the function is called.

  2. 否则,如果你确实希望函数接受多余的属性,则可以修改参数类型,以便通过使用 索引签名 明确允许多余的属性:

    ¥Otherwise, if you do want your function to accept excess properties, you can modify the parameter type in order to allow excess properties explicitly by using an index signature:

    interface SomeProperties {
    foo: string;

    // This allows any other properties.
    // You may wish to make these types more specific according to your use case.
    [key: PropertKey]: unknown;
    }

    function serialize(x: SomeProperties): string {
    return JSON.stringify(x);
    }

    // No error!
    serialize({ foo: 'bar', anotherProperty: 'baz' });

哪种解决方案合适取决于具体情况,具体取决于你的函数的预期用例。

¥Which solution is appropriate is a case-by-case decision, depending on the intended use case of your function.

我有一个规则报告的复杂场景,但我看不到如何删除类型参数。我该怎么办?

¥I have a complex scenario that is reported by the rule, but I can't see how to remove the type parameter. What should I do?

有时,你可能能够通过使用一些小众 TypeScript 功能(例如 NoInfer<T> 工具类型(参见 #9751))来重写代码。

¥Sometimes, you may be able to rewrite the code by reaching for some niche TypeScript features, such as the NoInfer<T> utility type (see #9751).

但是,很可能,你遇到了一种边缘情况,即类型以规则未考虑到的微妙方式使用。例如,以下神秘代码是一种测试两种类型是否相等的方法,并将由规则报告(参见 #9709):

¥But, quite possibly, you've hit an edge case where the type is being used in a subtle way that the rule doesn't account for. For example, the following arcane code is a way of testing whether two types are equal, and will be reported by the rule (see #9709):

type Compute<A> = A extends Function ? A : { [K in keyof A]: Compute<A[K]> };
type Equal<X, Y> =
(<T1>() => T1 extends Compute<X> ? 1 : 2) extends
(<T2>() => T2 extends Compute<Y> ? 1 : 2)
? true
: false;

在这种情况下,Equal 类型中创建的函数类型永远不会被分配;它们只是为了类型系统操作而创建的。这种用法不是规则旨在分析的。

¥In this case, the function types created within the Equal type are never expected to be assigned to; they're just created for the purpose of type system manipulations. This usage is not what the rule is intended to analyze.

在这种情况下,根据需要使用 eslint-disable 注释来抑制规则。

¥Use eslint-disable comments as appropriate to suppress the rule in these kinds of cases.

选项

该规则不可配置。

何时不使用它

¥When Not To Use It

此规则将报告仅使用类型参数来测试类型的函数,例如:

¥This rule will report on functions that use type parameters solely to test types, for example:

function assertType<T>(arg: T) {}

assertType<number>(123);
assertType<number>('abc');
// ~~~~~
// Argument of type 'string' is not assignable to parameter of type 'number'.

如果你使用此模式,则需要在测试类型的文件上禁用此规则。

¥If you're using this pattern then you'll want to disable this rule on files that test types.

进一步阅读

¥Further Reading

相关

¥Related To


Type checked lint rules are more powerful than traditional lint rules, but also require configuring type checked linting.

See Troubleshooting > Linting with Type Information > Performance if you experience performance degradations after enabling type checked rules.

'## 资源'