ban interface usage for component props and state

Summary: another eslint rule for flipper codebase

Reviewed By: passy

Differential Revision: D33917213

fbshipit-source-id: e60b867d359ef5b94a481edf0eda318ecff17eee
This commit is contained in:
Anton Kastritskiy
2022-02-02 05:08:48 -08:00
committed by Facebook GitHub Bot
parent 59b11c5f12
commit f2abbf63db
8 changed files with 61 additions and 9 deletions

View File

@@ -146,6 +146,7 @@ module.exports = {
'flipper/no-console-error-without-context': [2], 'flipper/no-console-error-without-context': [2],
'flipper/no-ts-file-extension': 2, 'flipper/no-ts-file-extension': 2,
'flipper/no-i-prefix-interfaces': 2, 'flipper/no-i-prefix-interfaces': 2,
'flipper/no-interface-props-or-state': 2,
'communist-spelling/communist-spelling': [1, {allow: ['cancelled']}], 'communist-spelling/communist-spelling': [1, {allow: ['cancelled']}],
// promise rules, see https://github.com/xjamundx/eslint-plugin-promise for details on each of them // promise rules, see https://github.com/xjamundx/eslint-plugin-promise for details on each of them

View File

@@ -22,6 +22,9 @@ import noTsFileExtension, {
import noIPrefixInterfaces, { import noIPrefixInterfaces, {
RULE_NAME as noIPrefixInterfacesRuleName, RULE_NAME as noIPrefixInterfacesRuleName,
} from './rules/noIPrefixInterfaces'; } from './rules/noIPrefixInterfaces';
import noInterfaceProps, {
RULE_NAME as noInterfacePropsRuleName,
} from './rules/noInterfacePropsOrState';
module.exports = { module.exports = {
rules: { rules: {
@@ -30,5 +33,6 @@ module.exports = {
[noConsoleErrorWithoutContextRuleName]: noConsoleErrorWithoutContext, [noConsoleErrorWithoutContextRuleName]: noConsoleErrorWithoutContext,
[noTsFileExtensionRuleName]: noTsFileExtension, [noTsFileExtensionRuleName]: noTsFileExtension,
[noIPrefixInterfacesRuleName]: noIPrefixInterfaces, [noIPrefixInterfacesRuleName]: noIPrefixInterfaces,
[noInterfacePropsRuleName]: noInterfaceProps,
}, },
}; };

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import {createESLintRule} from '../utils/createEslintRule';
type Options = [];
export type MessageIds = 'noInterfacePropsOrState';
export const RULE_NAME = 'no-interface-props-or-state';
export default createESLintRule<Options, MessageIds>({
name: RULE_NAME,
meta: {
type: 'problem',
docs: {
description: 'Use type aliases for component props instead of interfaces',
recommended: 'error',
},
schema: [],
messages: {
noInterfacePropsOrState:
'Use type aliases for component props and state instead of interfaces',
},
},
defaultOptions: [],
create(context) {
return {
TSInterfaceDeclaration(node) {
if (
!(node.id.name.endsWith('Props') || node.id.name.endsWith('State'))
) {
return;
}
context.report({
node: node.id,
messageId: 'noInterfacePropsOrState',
});
},
};
},
});

View File

@@ -213,12 +213,12 @@ export const DataList: (<T extends object>(
enableArrow: true, enableArrow: true,
}; };
interface DataListItemProps { type DataListItemProps = {
// TODO: add icon support // TODO: add icon support
title?: string | React.ReactElement; title?: string | React.ReactElement;
description?: string | React.ReactElement; description?: string | React.ReactElement;
enableArrow?: boolean; enableArrow?: boolean;
} };
const ArrowWrapper = styled.div({ const ArrowWrapper = styled.div({
flex: 0, flex: 0,

View File

@@ -53,7 +53,7 @@ import {debounce} from 'lodash';
import {useInUnitTest} from '../../utils/useInUnitTest'; import {useInUnitTest} from '../../utils/useInUnitTest';
import {createDataSource} from '../../state/createDataSource'; import {createDataSource} from '../../state/createDataSource';
interface DataTableBaseProps<T = any> { type DataTableBaseProps<T = any> = {
columns: DataTableColumn<T>[]; columns: DataTableColumn<T>[];
enableSearchbar?: boolean; enableSearchbar?: boolean;
enableAutoScroll?: boolean; enableAutoScroll?: boolean;
@@ -73,7 +73,7 @@ interface DataTableBaseProps<T = any> {
onRenderEmpty?: onRenderEmpty?:
| null | null
| ((dataSource?: DataSource<T, T[keyof T]>) => React.ReactElement); | ((dataSource?: DataSource<T, T[keyof T]>) => React.ReactElement);
} };
export type ItemRenderer<T> = ( export type ItemRenderer<T> = (
item: T, item: T,

View File

@@ -29,9 +29,9 @@ import {updateSettings} from '../reducers/settings';
import {switchPlugin} from '../reducers/pluginManager'; import {switchPlugin} from '../reducers/pluginManager';
import {awaitPluginCommandQueueEmpty} from '../dispatcher/pluginManager'; import {awaitPluginCommandQueueEmpty} from '../dispatcher/pluginManager';
interface PersistedState { type PersistedState = {
count: 1; count: 1;
} };
class TestPlugin extends FlipperPlugin<any, any, any> { class TestPlugin extends FlipperPlugin<any, any, any> {
static id = 'TestPlugin'; static id = 'TestPlugin';

View File

@@ -12,9 +12,9 @@ import {FlipperPlugin} from '../plugin';
import {TestIdler} from '../utils/Idler'; import {TestIdler} from '../utils/Idler';
import {getAllClients} from '../reducers/connections'; import {getAllClients} from '../reducers/connections';
interface PersistedState { type PersistedState = {
count: 1; count: 1;
} };
class TestPlugin extends FlipperPlugin<any, any, any> { class TestPlugin extends FlipperPlugin<any, any, any> {
static id = 'TestPlugin'; static id = 'TestPlugin';

View File

@@ -158,7 +158,6 @@ To start Flipper against a specific OnDemand instance, set FB_ONDEMAND flag, e.g
</FbInternalOnly> </FbInternalOnly>
## Guidelines for writing TypeScript ## Guidelines for writing TypeScript
* Prefer `type` for React props and state over interfaces
* Install 3rd party type definitions as dev dependency (e.g. `yarn add @types/lodash --dev`) * Install 3rd party type definitions as dev dependency (e.g. `yarn add @types/lodash --dev`)
## Submitting a diff / PR ## Submitting a diff / PR