You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Webpack构建的React+TypeScript项目中强制组件类命名规范?

Enforcing React Component Class Naming Rules in Ejected CRA (React+TS+Webpack)

Great question! Let’s break this down clearly—you absolutely can enforce this naming convention during your build process, and while it’s true basic ESLint/TSLint setups might not cover it out of the box, modern tooling lets you handle this (even for mixed TS/JS projects) without too much hassle. Here’s how to approach it:

First: ESLint Can Cover Most Cases (For Both TS and JS)

You mentioned thinking ESLint/TSLint can’t handle this, but with modern ESLint + TypeScript support, we can build a solution that works for both languages. Let’s start here:

Step 1: Combine Base Rules + Custom ESLint Rule

We need two things: enforce PascalCase (already covered by existing rules) and require the Component suffix (needs a custom rule).

  1. Base Capitalization Rules
    For TypeScript, use @typescript-eslint/class-name-casing to enforce PascalCase. For plain JS, use the new-cap rule. Add these to your ESLint config:

    {
      "rules": {
        "@typescript-eslint/class-name-casing": "error",
        "new-cap": ["error", { "newIsCap": true, "capIsNew": true }]
      }
    }
    
  2. Custom Rule for Component Suffix
    Since there’s no built-in rule to enforce the suffix, write a simple custom ESLint rule that checks if a class extending React’s component classes ends with Component. Here’s a working snippet you can add to your ESLint config (or wrap into a small plugin):

    module.exports = {
      create(context) {
        return {
          ClassDeclaration(node) {
            // Check if this class extends React.Component/PureComponent
            const isReactComponent = node.superClass?.type === 'MemberExpression' &&
              node.superClass.object.name === 'React' &&
              ['Component', 'PureComponent'].includes(node.superClass.property.name);
            
            if (isReactComponent && !node.id.name.endsWith('Component')) {
              context.report({
                node: node.id,
                message: 'React component class names must end with "Component"',
              });
            }
          }
        };
      }
    };
    

    For TypeScript, you can enhance this with @typescript-eslint/utils to use type information (instead of just checking the superclass name) for more accuracy—this catches components that use type aliases or indirect extensions.

  3. Make ESLint Fail the Build
    In your ejected CRA’s webpack.config.js, ensure ESLint runs as part of the build and errors out on violations. Use eslint-webpack-plugin (replacing the older eslint-loader) with strict settings:

    const ESLintPlugin = require('eslint-webpack-plugin');
    
    module.exports = {
      // ... rest of your config
      plugins: [
        new ESLintPlugin({
          extensions: ['js', 'jsx', 'ts', 'tsx'],
          emitError: true,
          failOnError: true,
        }),
      ],
    };
    

For TypeScript: Extra Strictness with Type Checks

If you want ironclad enforcement (e.g., catching components that don’t directly extend React.Component but are still typed as components), use a pre-build script with ts-morph—a tool that lets you programmatically analyze TypeScript code.

Step 1: Write a TS Scanning Script

Create a file like check-component-names.ts:

import { Project } from 'ts-morph';

const project = new Project({ tsConfigFilePath: './tsconfig.json' });

// Find all classes that are React components
const componentClasses = project.getSourceFiles().flatMap(file => 
  file.getClasses().filter(cls => {
    const baseType = cls.getBaseTypeAt(0)?.getText();
    return baseType === 'React.Component' || baseType === 'React.PureComponent';
  })
);

// Check for invalid names
const invalidComponents = componentClasses.filter(cls => !cls.getName()?.endsWith('Component'));

if (invalidComponents.length > 0) {
  console.error('❌ Invalid component class names found:');
  invalidComponents.forEach(cls => {
    console.error(`- ${cls.getName()} (in ${cls.getSourceFile().getBaseName()})`);
  });
  process.exit(1); // Exit with error code to fail the build
}

console.log('✅ All component class names follow the convention!');

Step 2: Add as a Pre-Build Step

Update your package.json scripts to run this before the build:

{
  "scripts": {
    "prebuild": "ts-node check-component-names.ts",
    "build": "react-scripts build"
  }
}

Do You Need Separate Methods for TS and JS?

Not entirely. ESLint can handle both languages with the right config. However:

  • For TypeScript, you can add the ts-morph script for more precise type-based checks that aren’t possible in plain JS.
  • For JS files, the ESLint custom rule (checking extension of React.Component) is sufficient since we don’t have type information to work with.

Final Tips

  • Test your rules with edge cases: HOC-wrapped components, components in index files, or components using rare React base classes.
  • If you use functional components, extend the ESLint rule to check function names too (your question focused on class components, but it’s easy to add).
  • Since TSLint is deprecated, stick with ESLint + @typescript-eslint for all TypeScript linting needs.

内容的提问来源于stack exchange,提问作者Estus Flask

火山引擎 最新活动