"local-rules/no-conditional-rendering-of-deferredspinner": "warn",
"local-rules/use-jest-mocked": "warn",
"local-rules/use-await-expect-async-matcher": "warn",
- "local-rules/no-implicit-coersion": "warn",
+ "local-rules/no-implicit-coercion": "warn",
"local-rules/no-api-imports": "warn"
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+const { RuleTester } = require('eslint');
+const noImplicitCoercion = require('../no-implicit-coercion');
+
+const ruleTester = new RuleTester({
+ parserOptions: {
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ parser: require.resolve('@typescript-eslint/parser'),
+});
+
+ruleTester.run('no-implicit-coercion', noImplicitCoercion, {
+ valid: [
+ {
+ code: `
+ function test(value?: number) {
+ if (value === undefined) {
+ return true;
+ }
+ }`,
+ },
+ {
+ code: `
+ function test(value?: number) {
+ if (Boolean(value)) {
+ return true;
+ }
+ }`,
+ },
+ {
+ code: `
+ function test(value?: {}) {
+ if (!value) {
+ return true;
+ }
+ }`,
+ },
+ {
+ code: `
+ function test(value: string) {
+ if (value !== '') {
+ return true;
+ }
+ }`,
+ },
+ {
+ code: `
+ function test(value?: number | {}) {
+ return value !== undefined && value.toString();
+ }`,
+ },
+ {
+ code: `
+ function test(value?: number) {
+ return value ?? 100;
+ }`,
+ },
+ {
+ code: `
+ interface Props {
+ test?: number;
+ }
+ function Test(props: Props) {
+ if (props.test !== undefined) {
+ return props.test * 10;
+ }
+ return 100;
+ }`,
+ },
+ {
+ code: `
+ interface Props {
+ test?: number;
+ check: boolean
+ }
+ function Test(props: Props) {
+ if (props.check && props.test !== undefined) {
+ return props.test * 10;
+ }
+ return 100;
+ }`,
+ },
+ {
+ code: `
+ interface Props {
+ test?: React.ReactNode;
+ }
+ function Test(props: Props) {
+ if (props.test) {
+ return props.test;
+ }
+ return null;
+ }`,
+ },
+ {
+ code: `
+ interface Props {
+ test?: number;
+ }
+ function Test(props: Props) {
+ return (
+ <div>
+ {props.test !== undefined && <span>{props.test}</span>}
+ {props.test === undefined && <span>100</span>}
+ </div>
+ );
+ }`,
+ },
+ ],
+ invalid: [
+ {
+ code: `
+ function test(value?: number) {
+ if (!value) {
+ return true;
+ }
+ }`,
+ errors: [{ messageId: 'noImplicitCoercion' }],
+ },
+ {
+ code: `
+ function test(value?: number) {
+ if (value) {
+ return true;
+ }
+ }`,
+ errors: [{ messageId: 'noImplicitCoercion' }],
+ },
+ {
+ code: `
+ function test(value: string) {
+ if (value) {
+ return true;
+ }
+ }`,
+ errors: [{ messageId: 'noImplicitCoercion' }],
+ },
+ {
+ code: `
+ function test(value?: number) {
+ return value && value > -1;
+ }`,
+ errors: [{ messageId: 'noImplicitCoercion' }],
+ },
+ {
+ code: `
+ function test(value?: string | {}) {
+ if (value) {
+ return 1;
+ }
+ }`,
+ errors: [{ messageId: 'noImplicitCoercion' }],
+ },
+ {
+ code: `
+ function test(value?: number) {
+ return value || 100;
+ }`,
+ errors: [{ messageId: 'noImplicitCoercion' }],
+ },
+ {
+ code: `
+ interface Props {
+ test?: number | {};
+ }
+ function Test(props: Props) {
+ return props.test && props.test.toString();
+ }`,
+ errors: [{ messageId: 'noImplicitCoercion' }],
+ },
+ {
+ code: `
+ interface Props {
+ test?: number;
+ }
+ function Test(props: Props) {
+ if (props.test) {
+ return props.test * 10;
+ }
+ return 100;
+ }`,
+ errors: [{ messageId: 'noImplicitCoercion' }],
+ },
+ {
+ code: `
+ interface Props {
+ test?: number;
+ check: boolean
+ }
+ function Test(props: Props) {
+ if (props.check && props.test) {
+ return props.test * 10;
+ }
+ return 100;
+ }`,
+ errors: [{ messageId: 'noImplicitCoercion' }],
+ },
+ {
+ code: `
+ interface Props {
+ test?: number;
+ }
+ function Test(props: Props) {
+ return (
+ <div>
+ {props.test && <span>{props.test}</span>}
+ {!props.test && <span>100</span>}
+ </div>
+ );
+ }`,
+ errors: [{ messageId: 'noImplicitCoercion' }, { messageId: 'noImplicitCoercion' }],
+ },
+ ],
+});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-const { RuleTester } = require('eslint');
-const noImplicitCoersion = require('../no-implicit-coersion');
-
-const ruleTester = new RuleTester({
- parserOptions: {
- ecmaFeatures: {
- jsx: true,
- },
- },
- parser: require.resolve('@typescript-eslint/parser'),
-});
-
-ruleTester.run('no-implicit-coersion', noImplicitCoersion, {
- valid: [
- {
- code: `
- function test(value?: number) {
- if (value === undefined) {
- return true;
- }
- }`,
- },
- {
- code: `
- function test(value?: number) {
- if (Boolean(value)) {
- return true;
- }
- }`,
- },
- {
- code: `
- function test(value?: {}) {
- if (!value) {
- return true;
- }
- }`,
- },
- {
- code: `
- function test(value: string) {
- if (value !== '') {
- return true;
- }
- }`,
- },
- {
- code: `
- function test(value?: number | {}) {
- return value !== undefined && value.toString();
- }`,
- },
- {
- code: `
- function test(value?: number) {
- return value ?? 100;
- }`,
- },
- {
- code: `
- interface Props {
- test?: number;
- }
- function Test(props: Props) {
- if (props.test !== undefined) {
- return props.test * 10;
- }
- return 100;
- }`,
- },
- {
- code: `
- interface Props {
- test?: number;
- check: boolean
- }
- function Test(props: Props) {
- if (props.check && props.test !== undefined) {
- return props.test * 10;
- }
- return 100;
- }`,
- },
- {
- code: `
- interface Props {
- test?: React.ReactNode;
- }
- function Test(props: Props) {
- if (props.test) {
- return props.test;
- }
- return null;
- }`,
- },
- {
- code: `
- interface Props {
- test?: number;
- }
- function Test(props: Props) {
- return (
- <div>
- {props.test !== undefined && <span>{props.test}</span>}
- {props.test === undefined && <span>100</span>}
- </div>
- );
- }`,
- },
- ],
- invalid: [
- {
- code: `
- function test(value?: number) {
- if (!value) {
- return true;
- }
- }`,
- errors: [{ messageId: 'noImplicitCoersion' }],
- },
- {
- code: `
- function test(value?: number) {
- if (value) {
- return true;
- }
- }`,
- errors: [{ messageId: 'noImplicitCoersion' }],
- },
- {
- code: `
- function test(value: string) {
- if (value) {
- return true;
- }
- }`,
- errors: [{ messageId: 'noImplicitCoersion' }],
- },
- {
- code: `
- function test(value?: number) {
- return value && value > -1;
- }`,
- errors: [{ messageId: 'noImplicitCoersion' }],
- },
- {
- code: `
- function test(value?: string | {}) {
- if (value) {
- return 1;
- }
- }`,
- errors: [{ messageId: 'noImplicitCoersion' }],
- },
- {
- code: `
- function test(value?: number) {
- return value || 100;
- }`,
- errors: [{ messageId: 'noImplicitCoersion' }],
- },
- {
- code: `
- interface Props {
- test?: number | {};
- }
- function Test(props: Props) {
- return props.test && props.test.toString();
- }`,
- errors: [{ messageId: 'noImplicitCoersion' }],
- },
- {
- code: `
- interface Props {
- test?: number;
- }
- function Test(props: Props) {
- if (props.test) {
- return props.test * 10;
- }
- return 100;
- }`,
- errors: [{ messageId: 'noImplicitCoersion' }],
- },
- {
- code: `
- interface Props {
- test?: number;
- check: boolean
- }
- function Test(props: Props) {
- if (props.check && props.test) {
- return props.test * 10;
- }
- return 100;
- }`,
- errors: [{ messageId: 'noImplicitCoersion' }],
- },
- {
- code: `
- interface Props {
- test?: number;
- }
- function Test(props: Props) {
- return (
- <div>
- {props.test && <span>{props.test}</span>}
- {!props.test && <span>100</span>}
- </div>
- );
- }`,
- errors: [{ messageId: 'noImplicitCoersion' }, { messageId: 'noImplicitCoersion' }],
- },
- ],
-});
'use-metrickey-enum': require('./use-metrickey-enum'),
'use-metrictype-enum': require('./use-metrictype-enum'),
'use-await-expect-async-matcher': require('./use-await-expect-async-matcher'),
- 'no-implicit-coersion': require('./no-implicit-coersion'),
+ 'no-implicit-coercion': require('./no-implicit-coercion'),
'no-api-imports': require('./no-api-imports'),
};
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+module.exports = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description:
+ 'Enforce using explicit comparison instead of implicit coercion for certain variable types',
+ category: 'Best Practices',
+ recommended: true,
+ },
+ messages: {
+ noImplicitCoercion:
+ 'Use explicit comparison instead of implicit coercion for strings and numbers.',
+ },
+ },
+ create(context) {
+ return {
+ UnaryExpression: (node) => {
+ const { argument, operator } = node;
+
+ if (operator === '!') {
+ checkImplicitCoercion(context, argument);
+ }
+ },
+ LogicalExpression: (node) => {
+ const { left, operator } = node;
+ if (operator === '??') {
+ return;
+ }
+ if (isVariableOrObjectField(left)) {
+ checkImplicitCoercion(context, left);
+ }
+ },
+ IfStatement: (node) => {
+ const { test } = node;
+ checkImplicitCoercion(context, test);
+ },
+ };
+ },
+};
+
+const isForbiddenType = (type) =>
+ type.intrinsicName === 'number' || type.intrinsicName === 'string';
+
+const isVariableOrObjectField = (node) =>
+ node.type === 'Identifier' || node.type === 'MemberExpression';
+
+function checkImplicitCoercion(context, argument) {
+ const tsNodeMap = context.parserServices.esTreeNodeToTSNodeMap;
+ const typeChecker = context.parserServices?.program?.getTypeChecker();
+ const type = typeChecker.getTypeAtLocation(tsNodeMap.get(argument));
+ if (type.aliasSymbol && type.aliasSymbol.name === 'ReactNode') {
+ return;
+ }
+ if (type.isUnion() ? type.types.some(isForbiddenType) : isForbiddenType(type)) {
+ context.report({
+ node: argument,
+ messageId: 'noImplicitCoercion',
+ });
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-module.exports = {
- meta: {
- type: 'suggestion',
- docs: {
- description:
- 'Enforce using explicit comparison instead of implicit coercion for certain variable types',
- category: 'Best Practices',
- recommended: true,
- },
- messages: {
- noImplicitCoersion:
- 'Use explicit comparison instead of implicit coercion for strings and numbers.',
- },
- },
- create(context) {
- return {
- UnaryExpression: (node) => {
- const { argument, operator } = node;
-
- if (operator === '!') {
- checkImplicitCoercion(context, argument);
- }
- },
- LogicalExpression: (node) => {
- const { left, operator } = node;
- if (operator === '??') {
- return;
- }
- if (isVariableOrObjectField(left)) {
- checkImplicitCoercion(context, left);
- }
- },
- IfStatement: (node) => {
- const { test } = node;
- checkImplicitCoercion(context, test);
- },
- };
- },
-};
-
-const isForbiddenType = (type) =>
- type.intrinsicName === 'number' || type.intrinsicName === 'string';
-
-const isVariableOrObjectField = (node) =>
- node.type === 'Identifier' || node.type === 'MemberExpression';
-
-function checkImplicitCoercion(context, argument) {
- const tsNodeMap = context.parserServices.esTreeNodeToTSNodeMap;
- const typeChecker = context.parserServices?.program?.getTypeChecker();
- const type = typeChecker.getTypeAtLocation(tsNodeMap.get(argument));
- if (type.aliasSymbol && type.aliasSymbol.name === 'ReactNode') {
- return;
- }
- if (type.isUnion() ? type.types.some(isForbiddenType) : isForbiddenType(type)) {
- context.report({
- node: argument,
- messageId: 'noImplicitCoersion',
- });
- }
-}