diff --git a/eslint-plugin-expensify/boolean-conditional-rendering.js b/eslint-plugin-expensify/boolean-conditional-rendering.js
new file mode 100644
index 0000000..113ed89
--- /dev/null
+++ b/eslint-plugin-expensify/boolean-conditional-rendering.js
@@ -0,0 +1,65 @@
+/* eslint-disable no-bitwise */
+const _ = require('underscore');
+const {ESLintUtils} = require('@typescript-eslint/utils');
+const ts = require('typescript');
+
+module.exports = {
+ name: 'boolean-conditional-rendering',
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'Enforce boolean conditions in React conditional rendering',
+ recommended: 'error',
+ },
+ schema: [],
+ messages: {
+ nonBooleanConditional: 'The left side of conditional rendering should be a boolean, not "{{type}}".',
+ },
+ },
+ defaultOptions: [],
+ create(context) {
+ function isJSXElement(node) {
+ return node.type === 'JSXElement' || node.type === 'JSXFragment';
+ }
+ function isBoolean(type) {
+ return (
+ (type.getFlags()
+ & (ts.TypeFlags.Boolean
+ | ts.TypeFlags.BooleanLike
+ | ts.TypeFlags.BooleanLiteral))
+ !== 0
+ || (type.isUnion()
+ && _.every(
+ type.types,
+ t => (t.getFlags()
+ & (ts.TypeFlags.Boolean
+ | ts.TypeFlags.BooleanLike
+ | ts.TypeFlags.BooleanLiteral))
+ !== 0,
+ ))
+ );
+ }
+ const parserServices = ESLintUtils.getParserServices(context);
+ const typeChecker = parserServices.program.getTypeChecker();
+ return {
+ LogicalExpression(node) {
+ if (!(node.operator === '&&' && isJSXElement(node.right))) {
+ return;
+ }
+ const leftType = typeChecker.getTypeAtLocation(
+ parserServices.esTreeNodeToTSNodeMap.get(node.left),
+ );
+ if (!isBoolean(leftType)) {
+ const baseType = typeChecker.getBaseTypeOfLiteralType(leftType);
+ context.report({
+ node: node.left,
+ messageId: 'nonBooleanConditional',
+ data: {
+ type: typeChecker.typeToString(baseType),
+ },
+ });
+ }
+ },
+ };
+ },
+};
diff --git a/eslint-plugin-expensify/tests/boolean-conditional-rendering.test.js b/eslint-plugin-expensify/tests/boolean-conditional-rendering.test.js
new file mode 100644
index 0000000..3fd05f5
--- /dev/null
+++ b/eslint-plugin-expensify/tests/boolean-conditional-rendering.test.js
@@ -0,0 +1,260 @@
+const RuleTester = require('@typescript-eslint/rule-tester').RuleTester;
+const rule = require('../boolean-conditional-rendering');
+
+const ruleTester = new RuleTester({
+ parser: '@typescript-eslint/parser',
+ parserOptions: {
+ project: './tsconfig.json',
+ tsconfigRootDir: __dirname,
+ sourceType: 'module',
+ ecmaVersion: 2020,
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+});
+
+ruleTester.run('boolean-conditional-rendering', rule, {
+ valid: [
+ {
+ code: `
+ const isActive = true;
+ isActive && ;
+ `,
+ },
+ {
+ code: `
+ const isActive = false;
+ isActive && ;
+ `,
+ },
+ {
+ code: `
+ const isVisible = Boolean(someValue);
+ isVisible && ;
+ `,
+ },
+ {
+ code: `
+ const user = { isLoggedIn: true, isBlocked: false };
+ const isAuthorized = user.isLoggedIn && !user.isBlocked;
+ isAuthorized && ;
+ `,
+ },
+ {
+ code: `
+ function isAuthenticated() { return true; }
+ isAuthenticated() && ;
+ `,
+ },
+ {
+ code: `
+ const isReady: boolean = true;
+ isReady && ;
+ `,
+ },
+ {
+ code: `
+ const isNotActive = !isActive;
+ isNotActive && ;
+ `,
+ },
+ {
+ code: `
+ const condition = !!someValue;
+ condition && ;
+ `,
+ },
+ {
+ code: `
+ const condition = someValue as boolean;
+ condition && ;
+ `,
+ },
+ {
+ code: `
+ enum Status { Active, Inactive }
+ const isActive = status === Status.Active;
+ isActive && ;
+ `,
+ },
+ {
+ code: `
+ const isAvailable = checkAvailability();
+ isAvailable && ;
+ function checkAvailability(): boolean { return true; }
+ `,
+ },
+ ],
+ invalid: [
+ {
+ code: `
+ const condition = "string";
+ condition && ;
+ `,
+ errors: [
+ {
+ messageId: 'nonBooleanConditional',
+ data: {type: 'string'},
+ },
+ ],
+ },
+ {
+ code: `
+ const condition = 42;
+ condition && ;
+ `,
+ errors: [
+ {
+ messageId: 'nonBooleanConditional',
+ data: {type: 'number'},
+ },
+ ],
+ },
+ {
+ code: `
+ const condition = [];
+ condition && ;
+ `,
+ errors: [
+ {
+ messageId: 'nonBooleanConditional',
+ data: {type: 'any[]'},
+ },
+ ],
+ },
+ {
+ code: `
+ const condition = {};
+ condition && ;
+ `,
+ errors: [
+ {
+ messageId: 'nonBooleanConditional',
+ data: {type: '{}'},
+ },
+ ],
+ },
+ {
+ code: `
+ const condition = null;
+ condition && ;
+ `,
+ errors: [
+ {
+ messageId: 'nonBooleanConditional',
+ data: {type: 'any'},
+ },
+ ],
+ },
+ {
+ code: `
+ const condition = undefined;
+ condition && ;
+ `,
+ errors: [
+ {
+ messageId: 'nonBooleanConditional',
+ data: {type: 'any'},
+ },
+ ],
+ },
+ {
+ code: `
+ const condition = () => {};
+ condition() && ;
+ `,
+ errors: [
+ {
+ messageId: 'nonBooleanConditional',
+ data: {type: 'void'},
+ },
+ ],
+ },
+ {
+ code: `
+ const condition: unknown = someValue;
+ condition && ;
+ `,
+ errors: [
+ {
+ messageId: 'nonBooleanConditional',
+ data: {type: 'unknown'},
+ },
+ ],
+ },
+ {
+ code: `
+ const condition: boolean | string = someValue;
+ condition && ;
+ `,
+ errors: [
+ {
+ messageId: 'nonBooleanConditional',
+ data: {type: 'string | boolean'},
+ },
+ ],
+ },
+ {
+ code: `
+ const condition = someObject?.property;
+ condition && ;
+ `,
+ errors: [
+ {
+ messageId: 'nonBooleanConditional',
+ data: {type: 'any'},
+ },
+ ],
+ },
+ {
+ code: `
+ enum Status { Active, Inactive }
+ const status = Status.Active;
+ status && ;
+ `,
+ errors: [
+ {
+ messageId: 'nonBooleanConditional',
+ data: {type: 'string'},
+ },
+ ],
+ },
+ {
+ code: `
+ const condition = Promise.resolve(true);
+ condition && ;
+ `,
+ errors: [
+ {
+ messageId: 'nonBooleanConditional',
+ data: {type: 'Promise'},
+ },
+ ],
+ },
+ {
+ code: `
+ function getValue() { return "value"; }
+ getValue() && ;
+ `,
+ errors: [
+ {
+ messageId: 'nonBooleanConditional',
+ data: {type: 'string'},
+ },
+ ],
+ },
+ {
+ code: `
+ const condition = someValue as string;
+ condition && ;
+ `,
+ errors: [
+ {
+ messageId: 'nonBooleanConditional',
+ data: {type: 'string'},
+ },
+ ],
+ },
+ ],
+});
diff --git a/eslint-plugin-expensify/tests/react.tsx b/eslint-plugin-expensify/tests/react.tsx
new file mode 100644
index 0000000..e69de29