Skip to main content

no-extraneous-class

Disallow classes used as namespaces.

🔒

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


此规则报告类何时没有非静态成员,例如专门用作静态命名空间的类。

英:This rule reports when a class has no non-static members, such as for a class used exclusively as a static namespace.

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

英:Users who come from a OOP paradigm may wrap their utility functions in an extra class, instead of putting them at the top level of an ECMAScript module. Doing so is generally unnecessary in JavaScript and TypeScript projects.

  • 封装类在不添加任何结构改进的情况下为代码增加了额外的认知复杂性
    • 无论要放在它们上面的是什么,例如效用函数,都已经通过模块进行了组织。
    • 作为替代方案,你可以 import * as ... 模块以将所有这些都放在一个对象中。
  • 当你开始输入属性名称时,IDE 无法为静态类或命名空间导入的属性提供良好的建议
  • 当未使用的变量等都在类中时,静态分析代码会更加困难(参见:在 TypeScript 中查找死代码(和死类型))。

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

英:This rule also reports classes that have only a constructor and no fields. Those classes can generally be replaced with a standalone function.

.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!');
}
}
Open in Playground

备择方案

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

英:Instead of using a static utility class we recommend you individually export the utilities from your module.

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

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

static util3() {
/* ... */
}
}
Open in Playground

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

英:If you strongly prefer to have all constructs from a module available as properties of a single object, you can import * as the module. This is known as a "namespace import". Namespace imports are sometimes preferable because they keep all properties nested and don't need to be changed as you start or stop using various properties from the module.

然而,命名空间导入受到以下缺点的影响:

英:However, namespace imports are impacted by these downsides:

  • 在现代打包器中,它们也不能很好地处理树摇动
  • 它们在使用每个属性之前需要名称前缀
// utilities.ts
export class Utilities {
static sayHello() {
console.log('Hello, world!');
}
}

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

Utilities.sayHello();
Open in Playground

关于修改变量的注释

你需要注意的一种情况是导出可变变量。 虽然类属性可以从外部改变,但导出的变量始终是常量。 这意味着导入器只能读取为其分配的第一个值,而无法写入变量。

英:One case you need to be careful of is exporting mutable variables. While class properties can be mutated externally, exported variables are always constant. This means that importers can only ever read the first value they are assigned and cannot write to the variables.

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

英:Needing to write to an exported variable is very rare and is generally considered a code smell. If you do need it you can accomplish it using getter and setter functions:

export class Utilities {
static mutableCount = 1;

static incrementCount() {
Utilities.mutableCount += 1;
}
}
Open in Playground

选项

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

英:This rule normally bans classes that are empty (have no constructor or fields). The rule's options each add an exemption for a specific type of class.

allowConstructorOnly

allowConstructorOnly 添加了对仅具有构造函数而没有字段的类的豁免。

英:allowConstructorOnly adds an exemption for classes that have only a constructor and no fields.

class NoFields {}
Open in Playground

allowEmpty

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

英:The allowEmpty option adds an exemption for classes that are entirely empty.

class NoFields {
constructor() {
console.log('Hello, world!');
}
}
Open in Playground

allowStaticOnly

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

英:The allowStaticOnly option adds an exemption for classes that only contain static members.

提醒

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

class EmptyClass {}
Open in Playground

allowWithDecorator

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

英:The allowWithDecorator option adds an exemption for classes that contain a member decorated with a @ decorator.

class Constants {
static readonly version = 42;
}
Open in Playground

何时不使用它

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

英:If your project was set up before modern class and namespace practices, and you don't have the time to switch over, you might not be practically able to use this rule. You might consider using ESLint disable comments for those specific situations instead of completely disabling this rule.

选项

该规则接受以下选项

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,
},
];

资源