gpt4 book ai didi

javascript - 使用 TypeScript 强制 React 组件命名

转载 作者:数据小太阳 更新时间:2023-10-29 04:58:45 31 4
gpt4 key购买 nike

有React+TypeScript的应用,所有的组件类都要大写,有Component后缀,例如:

export class FooBarComponent extends React.Component {...}

应用程序被弹出 create-react-application 应用程序,即使用 Webpack 构建。

如何强制组件命名与样式指南保持一致,至少对于组件类而言,当存在不一致时会在构建时抛出错误?

我相信这不能单独使用 TSLint/ESLint 来实现。如果应该对 TypeScript 和 JavaScript 使用不同的方法,那么针对这两种语言的解决方案会很有帮助。

最佳答案

我只能为您提供 typescript 的解决方案。

I believe this cannot be achieved with TSLint/ESLint alone.

有一个所谓的规则class-name可以部分解决您的问题,但似乎您需要为这种情况编写自定义规则。

所以让我们试着写这样的 custom tslint rule .为此,我们需要在 tslint 配置中使用 rulesDirectory 选项来指定自定义规则的路径

"rulesDirectory": [
"./tools/tslint-rules/"
],

因为我要在 typescript 中编写自定义规则,所以我将使用在 tslint@5.7.0 中添加的一项功能

[enhancement] custom lint rules will be resolved using node's path resolution to allow for loaders like ts-node (#3108)

我们需要安装ts-node

npm i -D ts-node

然后在tslint.json中添加fake规则

"ts-loader": true,

并在我们的规则目录中创建文件 tsLoaderRule.js:

const path = require('path');
const Lint = require('tslint');

// Custom rule that registers all of the custom rules, written in TypeScript, with ts-node.
// This is necessary, because `tslint` and IDEs won't execute any rules that aren't in a .js file.
require('ts-node').register({
project: path.join(__dirname, '../tsconfig.json')
});

// Add a noop rule so tslint doesn't complain.
exports.Rule = class Rule extends Lint.Rules.AbstractRule {
apply() {}
};

这基本上是一种广泛用于 Angular 包(如 Angular Material 、通用等)的方法

现在我们可以创建将用 typescript 编写的自定义规则(class-name 规则的扩展版本)。

myReactComponentRule.ts

import * as ts from 'typescript';
import * as Lint from 'tslint';

export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
static metadata: Lint.IRuleMetadata = {
ruleName: 'my-react-component',
description: 'Enforces PascalCased React component class.',
rationale: 'Makes it easy to differentiate classes from regular variables at a glance.',
optionsDescription: 'Not configurable.',
options: null,
optionExamples: [true],
type: 'style',
typescriptOnly: false,
};
/* tslint:enable:object-literal-sort-keys */

static FAILURE_STRING = (className: string) => `React component ${className} must be PascalCased and prefixed by Component`;

static validate(name: string): boolean {
return isUpperCase(name[0]) && !name.includes('_') && name.endsWith('Component');
}

apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk);
}
}

function walk(ctx: Lint.WalkContext<void>) {
return ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node): void {
if (isClassLikeDeclaration(node) && node.name !== undefined && isReactComponent(node)) {
if (!Rule.validate(node.name!.text)) {
ctx.addFailureAtNode(node.name!, Rule.FAILURE_STRING(node.name!.text));
}
}
return ts.forEachChild(node, cb);
});
}

function isClassLikeDeclaration(node: ts.Node): node is ts.ClassLikeDeclaration {
return node.kind === ts.SyntaxKind.ClassDeclaration ||
node.kind === ts.SyntaxKind.ClassExpression;
}

function isReactComponent(node: ts.Node): boolean {
let result = false;
const classDeclaration = <ts.ClassDeclaration> node;
if (classDeclaration.heritageClauses) {
classDeclaration.heritageClauses.forEach((hc) => {
if (hc.token === ts.SyntaxKind.ExtendsKeyword && hc.types) {

hc.types.forEach(type => {
if (type.getText() === 'React.Component') {
result = true;
}
});
}
});
}

return result;
}

function isUpperCase(str: string): boolean {
return str === str.toUpperCase();
}

最后,我们应该将新规则放入 tsling.json:

// Custom rules
"ts-loader": true,
"my-react-component": true

所以这样的代码

App extends React.Component

将导致:

enter image description here

我还创建了 ejected react-ts 可以试用的应用程序。

更新

I guess tracking class names in grandparents won't be a trivial task

我们确实可以处理继承。为此,我们需要创建从类 Lint.Rules.TypedRule 扩展的规则,以访问 TypeChecker:

myReactComponentRule.ts

import * as ts from 'typescript';
import * as Lint from 'tslint';

export class Rule extends Lint.Rules.TypedRule {
/* tslint:disable:object-literal-sort-keys */
static metadata: Lint.IRuleMetadata = {
ruleName: 'my-react-component',
description: 'Enforces PascalCased React component class.',
rationale: 'Makes it easy to differentiate classes from regular variables at a glance.',
optionsDescription: 'Not configurable.',
options: null,
optionExamples: [true],
type: 'style',
typescriptOnly: false,
};
/* tslint:enable:object-literal-sort-keys */

static FAILURE_STRING = (className: string) =>
`React component ${className} must be PascalCased and prefixed by Component`;

static validate(name: string): boolean {
return isUpperCase(name[0]) && !name.includes('_') && name.endsWith('Component');
}

applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
}
}

function walk(ctx: Lint.WalkContext<void>, tc: ts.TypeChecker) {
return ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node): void {
if (
isClassLikeDeclaration(node) && node.name !== undefined &&
containsType(tc.getTypeAtLocation(node), isReactComponentType) &&
!Rule.validate(node.name!.text)) {
ctx.addFailureAtNode(node.name!, Rule.FAILURE_STRING(node.name!.text));
}

return ts.forEachChild(node, cb);
});
}
/* tslint:disable:no-any */
function containsType(type: ts.Type, predicate: (symbol: any) => boolean): boolean {
if (type.symbol !== undefined && predicate(type.symbol)) {
return true;
}

const bases = type.getBaseTypes();
return bases && bases.some((t) => containsType(t, predicate));
}

function isReactComponentType(symbol: any) {
return symbol.name === 'Component' && symbol.parent && symbol.parent.name === 'React';
}
/* tslint:enable:no-any */

function isClassLikeDeclaration(node: ts.Node): node is ts.ClassLikeDeclaration {
return node.kind === ts.SyntaxKind.ClassDeclaration ||
node.kind === ts.SyntaxKind.ClassExpression;
}

function isUpperCase(str: string): boolean {
return str === str.toUpperCase();
}

另见提交:

关于javascript - 使用 TypeScript 强制 React 组件命名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49196812/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com