0012 Stricter Linter
Adding more strict lint rules to minimize issues in our codebase.
Summary
Our current linter rules are a bit loose, allowing us to make mistakes like using as any
, non-null assertions (shouldntBeNull!
), and redundant conditionals (true ? true : false
).
Although we are careful when reviewing PRs, these things can still slip through. Therefore, we need to introduce a few more rules to make our configuration stricter.
Motivation
To improve code predictability and maintainability using stricter, automated lint rules. This will also make PR reviews more efficient by automatically catching issues like as any
, saving developers from repeatedly providing the same feedback manually.
Solution
To achieve the goals, we need some new rules. Warnings will highlight potential issues without blocking commits initially, allowing for gradual adoption.
Accessibility Rules (Making Web Content Usable for Everyone)
useSemanticElements
(warn
)- Why: Enforces the use of HTML elements that convey meaning (like
<nav>
,<article>
,<button>
) over generic<div>
or<span>
elements for accessibility and SEO benefits. Screen readers and other assistive technologies rely on semantic markup. - Problematic Example:
- Preferred:
- Docs: Biome: useSemanticElements
- Why: Enforces the use of HTML elements that convey meaning (like
useFocusableInteractive
(warn
)- Why: Ensures that interactive elements that are focusable (can receive keyboard focus) are accessible via keyboard navigation. Elements with click handlers should generally be focusable or contained within focusable elements.
- Problematic Example:
- Preferred: Use inherently focusable elements like
<button>
or ensure custom interactive elements have appropriatetabIndex
(usually0
) and ARIA roles if needed. - Docs: Biome: useFocusableInteractive
Correctness Rules (Avoiding Errors & Improving Reliability)
noUnusedVariables
(error
)- Why: Prevents declaring variables that are never used, which clutters the code and can sometimes indicate incomplete logic or typos.
- Problematic Example:
- Preferred: Remove the unused variable.
- Docs: Biome: noUnusedVariables
useExhaustiveDependencies
(warn
)- Why: Specifically for React's Hooks (
useEffect
,useCallback
, etc.), this rule checks that all variables from the surrounding scope used inside the hook are included in the dependency array. Missing dependencies can lead to stale closures and unexpected behavior. - Problematic Example:
- Preferred: Include all dependencies identified by the linter.
- Docs: Biome: useExhaustiveDependencies
- Why: Specifically for React's Hooks (
noUnusedImports
(warn
)- Why: Flags imported variables, types, or modules that are not used anywhere in the file. This keeps the import list clean and avoids unnecessary dependencies.
- Problematic Example:
- Preferred: Remove the unused import.
- Docs: Biome: noUnusedImports
useJsxKeyInIterable
(warn
)- Why: Requires a unique
key
prop when rendering lists of elements in JSX using iterators likemap
. React uses these keys to efficiently update the list and maintain component state. - Problematic Example:
- Preferred: Add a unique and stable
key
to the outermost element returned by the map. - Docs: Biome: useJsxKeyInIterable
- Why: Requires a unique
noUnsafeOptionalChaining
(warn
)- Why: Prevents optional chaining (
?.
) in contexts where it doesn't provide safety, such as arithmetic operations or assignments, where anull
orundefined
result would likely cause a runtime error anyway. - Problematic Example:
- Preferred: Use nullish coalescing (
??
) or explicit checks to handle potentialnull
/undefined
before the operation. - Docs: Biome: noUnsafeOptionalChaining
- Why: Prevents optional chaining (
Security Rules
noDangerouslySetInnerHtml
(warn
)- Why: Prevents the use of
dangerouslySetInnerHTML
in JSX, which can expose your application to cross-site scripting (XSS) attacks if the injected HTML comes from user input. - Problematic Example:
- Preferred: Use safer methods to render dynamic content, like directly rendering text or using libraries that sanitize HTML.
- Docs: Biome: noDangerouslySetInnerHtml
- Why: Prevents the use of
Style Rules (Code Consistency & Readability)
useConst
(warn
)- Why: Encourages using
const
for variables that are never reassigned after their initial declaration. This improves readability by signaling the variable's immutability. - Problematic Example:
- Preferred:
- Docs: Biome: useConst
- Why: Encourages using
noNonNullAssertion
(warn
)- Why: Discourages the use of the non-null assertion operator (
!
), which tells TypeScript a value is notnull
orundefined
without actual checks. Overuse can hide potential runtime errors. This relates to theshouldntBeNull!
example mentioned earlier. - Problematic Example:
- Preferred: Use type guards, default values, or optional chaining (
user?.name
). - Docs: Biome: noNonNullAssertion
- Why: Discourages the use of the non-null assertion operator (
noUselessElse
(warn
)- Why: Prevents
else
blocks when theif
block contains areturn
,throw
,continue
, orbreak
statement, making the code less nested and easier to read. - Problematic Example:
- Preferred:
- Docs: Biome: noUselessElse
- Why: Prevents
useImportType
(warn
)- Why: Encourages using
import type
for importing only types. This clearly signals intent and can sometimes help build tools optimize imports. - Problematic Example:
- Preferred:
- Docs: Biome: useImportType
- Why: Encourages using
useFragmentSyntax
(warn
)- Why: Promotes the shorter
<>
syntax for React Fragments over<React.Fragment>
. - Problematic Example:
- Preferred:
- Docs: Biome: useFragmentSyntax
- Why: Promotes the shorter
useDefaultSwitchClause
(warn
)- Why: Enforces that
switch
statements have adefault
case, preventing potential errors if an unexpected value is encountered. - Problematic Example:
- Preferred:
- Docs: Biome: useDefaultSwitchClause
- Why: Enforces that
useAsConstAssertion
(warn
)- Why: Suggests using
as const
assertions for object and array literals when you want their properties/elements to be treated as specific literal types rather than general types (e.g.,string
instead of"ACTIVE"
). - Problematic Example:
- Preferred:
- Docs: Biome: useAsConstAssertion
- Why: Suggests using
useTemplate
(warn
)- Why: Prefers template literals (backticks
`
) over string concatenation (+
) for readability when embedding expressions. - Problematic Example:
- Preferred:
- Docs: Biome: useTemplate
- Why: Prefers template literals (backticks
useNamingConvention
(warn
)- Why: Enforces consistent naming conventions (e.g., camelCase for variables, PascalCase for classes/types) across the codebase, improving readability. Configuration might be needed to match team style.
- Problematic Example (depends on config):
- Preferred (typical JS/TS):
- Docs: Biome: useNamingConvention
noYodaExpression
(warn
)- Why: Prevents "Yoda" conditions where the literal/constant comes before the variable (e.g.,
if (5 === count)
). These can be less intuitive to read. - Problematic Example:
- Preferred:
- Docs: Biome: noYodaExpression
- Why: Prevents "Yoda" conditions where the literal/constant comes before the variable (e.g.,
noUnusedTemplateLiteral
(warn
)- Why: Flags template literals that don't contain any expressions, as regular string literals are simpler.
- Problematic Example:
- Preferred:
- Docs: Biome: noUnusedTemplateLiteral
noNegationElse
(warn
)- Why: Suggests refactoring
if
/else
statements where theif
condition is negated, often improving readability by handling the positive case first. - Problematic Example:
- Preferred:
- Docs: Biome: noNegationElse
- Why: Suggests refactoring
useSelfClosingElements
(warn
)- Why: Requires using self-closing tags for JSX elements with no children.
- Problematic Example:
- Preferred:
- Docs: Biome: useSelfClosingElements
useShorthandAssign
(warn
)- Why: Encourages using shorthand assignment operators (
+=
,-=
,*=
, etc.) for brevity. - Problematic Example:
- Preferred:
- Docs: Biome: useShorthandAssign
- Why: Encourages using shorthand assignment operators (
Suspicious Rules (Potential Logic Errors)
noDoubleEquals
(warn
)- Why: Discourages
==
and!=
in favor of the type-safe===
and!==
to avoid unexpected type coercion issues. - Problematic Example:
- Preferred:
- Docs: Biome: noDoubleEquals
- Why: Discourages
useIsArray
(warn
)- Why: Enforces the use of
Array.isArray()
to check for arrays instead ofinstanceof Array
, which can fail across different JavaScript execution contexts (e.g., iframes). - Problematic Example:
- Preferred:
- Docs: Biome: useIsArray
- Why: Enforces the use of
useAwait
(warn
)- Why: Flags
async
functions that don't useawait
, as theasync
keyword might be unnecessary or indicate a missedawait
. - Problematic Example:
- Preferred:
- Docs: Biome: useAwait
- Why: Flags
noFallthroughSwitchClause
(warn
)- Why: Prevents accidental fall-through in
switch
statements by requiringbreak
,return
,throw
, orcontinue
at the end of non-emptycase
blocks (unless explicitly commented// biome-ignore lint/suspicious/noFallthroughSwitchClause: <explanation>
). - Problematic Example:
- Preferred:
- Docs: Biome: noFallthroughSwitchClause
- Why: Prevents accidental fall-through in
noExplicitAny
(warn
)- Why: Discourages the explicit use of
any
as a type, as it effectively disables TypeScript's type checking for that variable. This relates to theas any
example mentioned earlier. - Problematic Example:
- Preferred: Use specific types, generics, or
unknown
(which requires type checking before use). - Docs: Biome: noExplicitAny
- Why: Discourages the explicit use of
noConsoleLog
(warn
)- Why: Flags
console.log
(and otherconsole
methods) to prevent debug statements from being accidentally committed to production code. Consider using a dedicated logger or removing logs before merging. - Problematic Example:
- Preferred: Remove the log or use a proper logging library.
- Docs: Biome: noConsoleLog
- Why: Flags
Complexity Rules (Simplifying Code)
noUselessTernary
(warn
)- Why: Prevents ternary operators that directly return boolean literals (
true
/false
) based on a condition, as the condition itself can be used. This relates to thetrue ? true : false
example. - Problematic Example:
- Preferred:
- Docs: Biome: noUselessTernary
- Why: Prevents ternary operators that directly return boolean literals (
noUselessTypeConstraint
(warn
)- Why: Flags redundant type constraints in generics like
T extends any
orT extends unknown
, which provide no additional limitation. - Problematic Example:
- Preferred:
- Docs: Biome: noUselessTypeConstraint
- Why: Flags redundant type constraints in generics like
useSimplifiedLogicExpression
(warn
)- Why: Encourages simplifying boolean expressions, such as removing double negations (
!!
). - Problematic Example:
- Preferred:
- Docs: Biome: useSimplifiedLogicExpression
- Why: Encourages simplifying boolean expressions, such as removing double negations (
noUselessStringConcat
(warn
)- Why: Prevents concatenating two string literals, which should just be combined into a single literal.
- Problematic Example:
- Preferred:
- Docs: Biome: noUselessStringConcat
useOptionalChain
(warn
)- Why: Promotes using the optional chaining operator (
?.
) instead of longer logical AND (&&
) chains for accessing nested properties safely. - Problematic Example:
- Preferred:
- Docs: Biome: useOptionalChain
- Why: Promotes using the optional chaining operator (
useDateNow
(warn
)- Why: Suggests using
Date.now()
which is slightly more performant and concise thannew Date().getTime()
or+new Date()
for getting a timestamp. - Problematic Example:
- Preferred:
- Docs: Biome: useDateNow
- Why: Suggests using
noExtraBooleanCast
(warn
)- Why: Prevents unnecessary boolean casts (using
Boolean()
or!!
) in contexts where the value is already treated as a boolean (likeif
statements or logical operators). - Problematic Example:
- Preferred:
- Docs: Biome: noExtraBooleanCast
- Why: Prevents unnecessary boolean casts (using
By enabling these rules, we aim to catch more potential errors and enforce stylistic consistency automatically.