跳至主要内容

禁止多余的类

禁止将类用作命名空间。

🔒

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

当一个类没有非静态成员时,例如用于作为静态命名空间的类,此规则会报告。

来自 OOP 范式的用户可能会将他们的实用函数包装在一个额外的类中,而不是将它们放在 ECMAScript 模块的顶层。在 JavaScript 和 TypeScript 项目中,这样做通常是不必要的。

  • 包装类在不添加任何结构改进的情况下增加了代码的认知复杂度。
    • 无论放在它们上面什么,例如实用函数,由于它们位于模块中,因此已经组织好了。
    • 作为替代方案,您可以 import * as ... 模块以将所有内容都放在单个对象中。
  • 当您开始键入属性名称时,IDE 无法为静态类或命名空间导入的属性提供更好的建议。
  • 当所有变量都位于类上时,更难静态分析代码以查找未使用的变量等(参见:在 TypeScript 中查找死代码(和死类型))。

此规则还报告仅具有构造函数且没有字段的类。这些类通常可以用独立函数替换。

.eslintrc.cjs
module.exports = {
"rules": {
"@typescript-eslint/no-extraneous-class": "error"
}
};

在游乐场中尝试此规则 ↗

示例

class StaticConstants {
static readonly version = 42;

static isProduction() {
return process.env.NODE_ENV === 'production';
}
}

class HelloWorldLogger {
constructor() {
console.log('Hello, world!');
}
}
在游乐场中打开

替代方案

我们建议您从模块中单独导出实用程序,而不是使用静态实用程序类。

export class Utilities {
static util1() {
return Utilities.util3();
}

static util2() {
/* ... */
}

static util3() {
/* ... */
}
}
在游乐场中打开

如果您强烈希望将模块中的所有构造都作为单个对象的属性提供,您可以 import * as 模块。这被称为“命名空间导入”。命名空间导入有时更可取,因为它们将所有属性嵌套在一起,并且不需要在您开始或停止使用模块中的各种属性时进行更改。

但是,命名空间导入会受到以下缺点的影响

  • 它们在现代打包器中也不太适合树摇。
  • 它们需要在每个属性使用之前添加一个名称前缀。
// utilities.ts
export class Utilities {
static sayHello() {
console.log('Hello, world!');
}
}

// consumers.ts
import { Utilities } from './utilities';

Utilities.sayHello();
在游乐场中打开

关于可变变量的说明

您需要注意的一种情况是导出可变变量。虽然类属性可以在外部被修改,但导出的变量始终是常量。这意味着导入者只能读取他们被分配的第一个值,而不能写入这些变量。

需要写入导出变量的情况非常罕见,通常被认为是代码异味。如果您确实需要它,可以使用 getter 和 setter 函数来实现。

export class Utilities {
static mutableCount = 1;

static incrementCount() {
Utilities.mutableCount += 1;
}
}
在游乐场中打开

选项

此规则接受以下选项。

type Options = [
{
/** Whether to allow extraneous classes that contain only a constructor. */
allowConstructorOnly?: boolean;
/** Whether to allow extraneous classes that have no body (i.e. are empty). */
allowEmpty?: boolean;
/** Whether to allow extraneous classes that only contain static members. */
allowStaticOnly?: boolean;
/** Whether to allow extraneous classes that include a decorator. */
allowWithDecorator?: boolean;
},
];

const defaultOptions: Options = [
{
allowConstructorOnly: false,
allowEmpty: false,
allowStaticOnly: false,
allowWithDecorator: false,
},
];

此规则通常禁止空的类(没有构造函数或字段)。该规则的选项分别为特定类型的类添加了豁免。

allowConstructorOnly

allowConstructorOnly 为只有构造函数而没有字段的类添加了豁免。

class NoFields {}
在游乐场中打开

allowEmpty

allowEmpty 选项为完全为空的类添加了豁免。

class NoFields {
constructor() {
console.log('Hello, world!');
}
}
在游乐场中打开

allowStaticOnly

allowStaticOnly 选项为仅包含静态成员的类添加了豁免。

注意

我们强烈建议不要使用 allowStaticOnly 豁免。它违背了此规则的主要目的,即阻止仅用于静态成员的类。

class EmptyClass {}
在游乐场中打开

allowWithDecorator

allowWithDecorator 选项为用 @ 装饰器装饰的类添加了豁免。

class Constants {
static readonly version = 42;
}
在游乐场中打开

何时不使用它

如果您的项目是在现代类和命名空间实践出现之前创建的,并且您没有时间切换,那么您可能无法实际使用此规则。您可以考虑使用 ESLint 禁用注释 针对这些特定情况,而不是完全禁用此规则。

资源