Skip to content

Conversation

@arnold095
Copy link

@arnold095 arnold095 commented Nov 24, 2025

I used claude sonnet 4.5 and chatgpt to help me with

Summary

The current rule is too strict.

Scenarios Implemented ✅

This PR adds support for 8 categories of trivially inferrable expressions:

  1. Binary expressions: const sum = 1 + 1 → infers number
  2. Comparison expressions: const isEqual = 'a' === 'b' → infers boolean
  3. Logical expressions: const and = true && false → infers boolean
  4. Class instantiation: const date = new Date() → infers Date
  5. Array literals: const arr = [1, 2, 3] → infers number[]
  6. Conditional expressions: const val = true ? 'yes' : 'no' → infers string
  7. Function calls: const num = Math.random() → infers number (allows all calls - Option A)
  8. Parameter defaults: const fn = (word = 'hello') => word → infers string for word

Scenarios NOT Implemented ❌

  1. Enum member access in nested objects
enum Status {
    Active = "Active",
}
const condition = [{ Status: { $ne: Status.Active } }];
  1. Direct variable assignments:
const foo = true;
const bar = foo;
  1. Variables in conditionals:
const foo = true;
const value1 = 'test';
const value2 = 'test2';
const bar = foo ? value1 : value2;
  1. Enum member access in object properties:
enum Keys {
    A = 'A',
    B = 'B',
}
const mapping = {
    A: Keys.A,
    B: Keys.B,
};
  1. Class reference in arrays
class Foo {}
const classes = [Foo]; 
  1. Object destructuring from typed source
const object = {
    Min: 1,
    Max: 6,
};
const { Min, Max } = object;

Test Plan

Docs

closes #7606

@changeset-bot
Copy link

changeset-bot bot commented Nov 24, 2025

🦋 Changeset detected

Latest commit: c5e4353

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added A-Linter Area: linter L-JavaScript Language: JavaScript and super languages labels Nov 24, 2025
@arnold095
Copy link
Author

Apart from implementing this PR, I believe that the ‘any’ type validation should not be included in this rule. This violates the SRP, the rule should only ensure types are present, not validate their quality.

// Currently: ❌ Error from useExplicitType
const fn = (arg: any): string => `test ${arg}`;

// Proposed: ✅ OK (has explicit type, quality is not this rule's concern)
const fn = (arg: any): string => `test ${arg}`;

IMHO the type quality should be handled by dedicated rules (like noExplicitAny), following the pattern of ESLint TypeScript (explicit-function-return-type vs no-explicit-any).

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 24, 2025

Walkthrough

Removed the AnyParameter variant from ViolationKind and its message/advice branches. Added helpers to classify trivially inferrable expressions (binary, logical, array, conditional) and extended allowed untyped expressions to include parenthesised expressions, new (constructor) expressions, call expressions, and the above trivially inferrable kinds. Parameters with initialisers are now considered typed (no diagnostic); only parameters without explicit type and without initializer remain violations. Tests and a changeset relaxing the useExplicitType rule were added.

Suggested labels

A-Diagnostic

Suggested reviewers

  • dyc3
  • ematipico

Pre-merge checks and finishing touches

✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: relaxing the useExplicitType rule to accept trivially inferrable types, which aligns with the core changeset across all modified files.
Linked Issues check ✅ Passed The PR directly addresses issue #7606 by implementing relaxation of useExplicitType rule for trivially inferrable types, covering most key requirements: binary/comparison/logical expressions, class instantiation, array literals, conditional expressions, function calls, and parameter defaults [#7606].
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the rule relaxation: updates to the lint rule logic, test files with new test cases demonstrating trivially inferrable patterns, and the changeset entry documenting the feature—no extraneous modifications detected.
Description check ✅ Passed The PR description clearly relates to the changeset, describing the relaxation of the useExplicitType rule with specific examples of trivially inferrable expressions now permitted.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs (1)

8-13: New trivial-expression helpers fit the rule’s intent

The added imports and helpers for binary/logical/array/conditional expressions slot cleanly into is_allowed_in_untyped_expression and match the goal of allowing obviously inferrable top-level initialisers and RHS expressions (including new and simple calls). The recursion via is_allowed_in_untyped_expression keeps the logic centralised without introducing extra branching or deep nesting, which is nicely in line with the analyse-contrib guidance.

Also applies to: 1012-1110, 1141-1187

crates/biome_js_analyze/tests/specs/nursery/useExplicitType/valid.ts (1)

203-280: Good coverage for trivially inferrable expressions; consider pinning non-literal defaults

This block does a solid job of exercising all the newly-allowed categories (binaries, comparisons, new, arrays, conditionals, calls, and literal-based parameter defaults). To fully lock in the intended behaviour, I’d add at least one example with a non-literal parameter default (e.g. value = someFunc()) in either valid.ts or invalid.ts, so tests make it explicit whether those are meant to be allowed after the rule change.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98ca2ae and 37410a9.

⛔ Files ignored due to path filters (2)
  • crates/biome_js_analyze/tests/specs/nursery/useExplicitType/invalid.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useExplicitType/valid.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (3)
  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs (4 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useExplicitType/invalid.ts (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useExplicitType/valid.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use the Rust dbg!() macro for debugging output during test execution, and pass the --show-output flag to cargo test to display debug output.
Use snapshot testing with the insta crate for testing in Rust projects. Accept or reject snapshots using cargo insta accept, cargo insta reject, or cargo insta review.
Write doc comments as doc tests in Rust using code blocks with assertions that will be executed during the testing phase.
Use rustdoc inline documentation for rules, assists, and their options. Create corresponding documentation PRs for other documentation updates against the next branch of the website repository.
Set the version metadata field in linter rule implementations to 'next' for newly created rules. Update this field to the new version number when releasing a minor or major version.

Files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
🧠 Learnings (17)
📚 Learning: 2025-11-24T18:04:42.146Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:04:42.146Z
Learning: Applies to crates/biome_analyze/**/biome_*_analyze/tests/specs/**/*.{js,ts,tsx,jsx,json,css,graphql} : Test files should use 'invalid' or 'valid' prefixes to indicate whether they contain code reported by the rule

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useExplicitType/valid.ts
📚 Learning: 2025-11-24T18:05:42.338Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.338Z
Learning: Applies to crates/biome_js_type_info/**/local_inference.rs : Implement local inference in dedicated modules to derive type definitions from expressions without context of surrounding scopes

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useExplicitType/valid.ts
  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:05:42.338Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.338Z
Learning: Applies to crates/biome_js_type_info/**/*.rs : Distinguish between `TypeData::Unknown` and `TypeData::UnknownKeyword` to measure inference effectiveness versus explicit user-provided unknown types

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useExplicitType/valid.ts
  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:05:42.338Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.338Z
Learning: Applies to crates/biome_js_type_info/**/js_module_info/collector.rs : Implement module-level (thin) inference to resolve `TypeReference::Qualifier` variants by looking up declarations in module scopes and handling import statements

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useExplicitType/valid.ts
  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:05:42.338Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.338Z
Learning: Applies to crates/biome_js_type_info/**/flattening.rs : Implement type flattening to simplify `TypeofExpression` variants once all component types are resolved

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:05:42.338Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.338Z
Learning: Applies to crates/biome_js_type_info/**/*.rs : Use `TypeData::Unknown` to indicate when type inference falls short or is not implemented

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:05:42.338Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.338Z
Learning: Applies to crates/biome_js_type_info/**/*.rs : Use `TypeReference` variants (`Qualifier`, `Resolved`, `Import`, `Unknown`) to represent different phases of type resolution

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:05:42.337Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.337Z
Learning: Applies to crates/biome_js_type_info/**/*.rs : Use `TypeReference` instead of `Arc` for types that reference other types to avoid stale cache issues when modules are replaced

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:04:42.146Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:04:42.146Z
Learning: Applies to crates/biome_analyze/**/biome_*_analyze/lib/src/**/!(mod).rs : Avoid deep indentation in rule implementations by using Rust helper functions like 'map', 'filter', and 'and_then' instead of nested if-let statements

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:04:42.146Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:04:42.146Z
Learning: Applies to crates/biome_analyze/**/biome_*_analyze/lib/src/**/!(mod).rs : Rule implementation should use 'Type Query = Ast<NodeType>' to query the AST/CST for specific node types

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:04:42.146Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:04:42.146Z
Learning: Applies to crates/biome_analyze/**/biome_*_analyze/lib/src/**/!(mod).rs : Rules that report unnecessary code that could be removed or simplified should use the 'noUseless<Concept>' naming convention (e.g., `noUselessConstructor`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:04:42.146Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:04:42.146Z
Learning: Applies to crates/biome_analyze/**/biome_*_analyze/lib/src/**/!(mod).rs : Rules should only have severity set to 'error' if they report hard errors, dangerous code, or accessibility issues; use 'warn' for possibly erroneous code; use 'info' for stylistic suggestions

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:04:42.146Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:04:42.146Z
Learning: Applies to crates/biome_analyze/**/biome_*_analyze/lib/src/**/!(mod).rs : Rules should use the semantic query 'Type Query = Semantic<NodeType>' to access semantic information like bindings and references

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:06:03.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.536Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `ConditionalParsedSyntax` for syntax that is only valid in specific contexts (e.g., strict mode, file types, language versions) and call `or_invalid_to_bogus()` to convert to a bogus node if not supported

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:04:42.146Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:04:42.146Z
Learning: Applies to crates/biome_analyze/**/biome_*_analyze/lib/src/**/!(mod).rs : The 'language' field in 'declare_lint_rule!' should be set to the specific JavaScript dialect (jsx, ts, tsx) if the rule only applies to that dialect, otherwise use 'js'

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:04:57.290Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_diagnostics/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:04:57.290Z
Learning: Applies to crates/biome_diagnostics/**/*.rs : Implement the Diagnostic trait on types, or use the #[derive(Diagnostic)] procedural macro to implement the trait. Configure category, severity, description, message, location, and tags using the #[diagnostic] attribute

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:04:42.146Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:04:42.146Z
Learning: Applies to crates/biome_analyze/**/biome_*_analyze/lib/src/**/!(mod).rs : Rules that ban functions or variables should use the semantic model to check if the variable is global before reporting, to avoid false positives on locally redeclared variables

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
🔇 Additional comments (1)
crates/biome_js_analyze/tests/specs/nursery/useExplicitType/invalid.ts (1)

160-160: Nice mixed-parameter invalid case

This neatly checks the “one untyped, one defaulted” parameter scenario against the relaxed rule; no issues from me.

Comment on lines 1256 to 1264
// If parameter has an initializer with a trivially inferrable type, allow it
if let Some(initializer) = parameter.initializer()
&& let Ok(expr) = initializer.expression()
{
let expr = expr.omit_parentheses();
if is_allowed_in_untyped_expression(&expr, false) {
return None;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Parameter defaults now allow non-literal expressions as “trivially inferrable”

Using is_allowed_in_untyped_expression(&expr, false) for parameters means defaults like value = someFunc(), value = new Date(), or other non-literal expressions will be treated as safe to leave untyped, because call and new expressions (and some composites) are all now whitelisted.

That appears to contradict the PR description, which explicitly calls out non-literal parameter defaults as intentionally not implemented. If you do want to keep that restriction, this branch probably needs a narrower predicate (e.g. only simple literal defaults, possibly excluding null / undefined) instead of delegating to the full untyped-expression machinery. Otherwise, it would be good to clarify the docs/PR text and add tests to confirm that non-literal defaults are intentionally allowed.

@dyc3
Copy link
Contributor

dyc3 commented Nov 24, 2025

This will at least need a changeset.

@arnold095 arnold095 changed the title feat(useExplicitType): Relax rule for trivially inferrable types feat(useExplicitType): relax rule for trivially inferrable types Nov 30, 2025
@arnold095
Copy link
Author

@dyc3 I have added the changeset. Is there anything else needed to merge this branch?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
.changeset/relax-use-explicit-type.md (1)

5-15: Polish the changeset format to match guidelines.

The title needs header markup, the description could be more concise, and adding a code example would illustrate the change well. Per coding guidelines, changesets should use #### or ##### headers, keep descriptions to 1–3 sentences, and demonstrate rule changes with examples.

Consider restructuring as follows:

-feat(useExplicitType): Relax rule for trivially inferrable types
+#### feat(useExplicitType): Relax rule for trivially inferrable types

-Allow type annotations to be omitted when types are trivially inferrable from:
+Biome now allows type annotations to be omitted for trivially inferrable expressions such as binary operations, array literals, function calls, and more.
+
+Previously invalid:
+
+```ts
+const sum = 1 + 1;              // error: missing type annotation
+const arr = [1, 2, 3];          // error: missing type annotation
+```
+
+Now valid:
+
+```ts
+const sum = 1 + 1;              // okay: number is trivially inferrable
+const arr = [1, 2, 3];          // okay: number[] is trivially inferrable
+```
+
+This applies to:
 - Binary expressions (`const sum = 1 + 1`)
 - Comparison expressions (`const isEqual = 'a' === 'b'`)
 - Logical expressions (`const and = true && false`)
 - Class instantiation (`const date = new Date()`)
 - Array literals (`const arr = [1, 2, 3]`)
 - Conditional expressions (`const val = true ? 'yes' : 'no'`)
 - Function calls (`const num = Math.random()`)
-- Parameter defaults (`const fn = (word = 'hello') => word`)
+- Parameter defaults (`const fn = (word = 'hello') => word`).
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37410a9 and b113dbe.

⛔ Files ignored due to path filters (2)
  • crates/biome_js_analyze/tests/specs/nursery/useExplicitType/invalid.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useExplicitType/valid.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (1)
  • .changeset/relax-use-explicit-type.md (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
.changeset/**/*.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

.changeset/**/*.md: Create changesets for user-facing changes using just new-changeset; use headers with #### or ##### only; keep descriptions concise (1-3 sentences) and focus on user-facing changes
Use past tense when describing what was done ('Added new feature'), present tense when describing Biome behavior ('Biome now supports'); end sentences with a full stop
For new lint rules, show an example of an invalid case in an inline code snippet or code block; for rule changes, demonstrate what is now invalid or valid; for formatter changes, use a diff code block

Files:

  • .changeset/relax-use-explicit-type.md

@arnold095
Copy link
Author

Hey @dyc3 @ematipico 👋

I've also removed the any type validation from useExplicitType. This was redundant since noExplicitAny already handles this case.
This follows the Single Responsibility Principle — useExplicitType should only check for the presence of type annotations, not their quality.

Let me know if you have any concerns with this approach!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
crates/biome_js_analyze/tests/specs/nursery/useExplicitType/valid.ts (1)

281-300: Non-literal default params are now “blessed” – please confirm that’s intentional

These examples, especially setup/fetchData and process = (value = someVariable), together with the new parameter_has_not_type logic, mean any parameter with a default is treated as “typed enough”, regardless of how complex or opaque the default is.

That’s broader than the PR text, which said non-literal defaults would be excluded. If this is the new policy, it’d be good to update the PR/Docs and maybe call it out in the changeset; if not, you probably want tests here that only accept literal/const defaults and fail things like value = someFunc() / value = someVariable.

Please double-check that this matches the intended scope for “trivially inferrable” defaults and, if so, consider documenting it explicitly so users aren’t surprised.

crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs (1)

1230-1245: Any parameter with a default now escapes the rule – this is a bigger behavioural jump than the description suggests

parameter_has_not_type now treats every defaulted parameter as acceptable:

if parameter.type_annotation().is_some() {
    return None;
}
if parameter.initializer().is_some() {
    return None;
}

Together with the new valid.ts cases, this means all of these are fine:

  • const fn = (word = 'hello') => …
  • const retry = (max = MAX_ATTEMPTS) => …
  • const setup = (config = DEFAULT_CONFIG) => …
  • const process = (value = someVariable) => …
  • and even const fn = (v = someFunc()) => … or v = new Date().

That goes beyond the original PR text (which said non-literal defaults would be excluded) and also beyond the earlier implementation that at least tried to classify defaults as “trivially inferrable”. You’ve effectively redefined UseExplicitType so that “has any default” counts as having enough type information, even though there is no annotation anywhere.

If that’s the hill we’re choosing to stand on, I’d strongly suggest:

  • Calling this out explicitly in the rule docs / changeset, and
  • Possibly adding a couple of “wild” defaults to invalid.ts (or comments) to show that this permissiveness is deliberate, not an oversight.

If instead you still want to keep non-trivial defaults out, this function probably needs to inspect the default expression (e.g. only allow simple literals/const identifiers, or reuse a narrower is_allowed_in_untyped_expression flavour) rather than short-circuiting on initializer().is_some().

Could you confirm whether defaulted-but-unannotated parameters are now always considered acceptable, and, if so, update the public contract accordingly so downstream users don’t get surprised when upgrading?

🧹 Nitpick comments (1)
crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs (1)

999-1175: Trivial-expression helpers look solid, but new/call now silently relax the rule semantics

The new helpers for binary/logical/array/conditional expressions are nicely factored and keep the recursion local to genuinely “simple” shapes; they’re a good fit for the relaxed rule.

One thing to flag: is_allowed_in_untyped_expression now unconditionally returns true for any JsNewExpression or JsCallExpression. That means top-level declarations like:

  • const direct = fn();
  • const nested = { result: fn() };

are no longer reported as untyped variables, even though the big rule doc comment still lists essentially that pattern as invalid. If you’re deliberately going with “Option A: allow all calls/new so we stop nagging about zod schemas etc.” that’s fine, but then the docs and examples should say so (and tests should assert the new behaviour explicitly).

If the intent was to stay stricter for user-defined calls while only relaxing obvious built-ins, this branch probably wants a narrower predicate (e.g. has_trivially_inferrable_type or some whitelisting) rather than matches!(expr, JsCallExpression(_)).

Please confirm whether “all calls/new are OK for untyped top-level vars” is now part of the contract for useExplicitType; if yes, updating the inline examples and user docs will save folks some head-scratching.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e39894 and c5e4353.

⛔ Files ignored due to path filters (1)
  • crates/biome_js_analyze/tests/specs/nursery/useExplicitType/valid.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (3)
  • .changeset/relax-use-explicit-type.md (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs (4 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useExplicitType/valid.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .changeset/relax-use-explicit-type.md
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

📄 CodeRabbit inference engine (CONTRIBUTING.md)

For Node.js package development, build WebAssembly bindings and JSON-RPC bindings; run tests against compiled files after implementing features or bug fixes

Files:

  • crates/biome_js_analyze/tests/specs/nursery/useExplicitType/valid.ts
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use the dbg!() macro for debugging output during testing, and pass the --show-output flag to cargo to view debug output
Use cargo t or cargo test to run tests; for a single test, pass the test name after the test command
Use snapshot testing with the insta crate; run cargo insta accept, cargo insta reject, or cargo insta review to manage snapshot changes
Write doctests as doc comments with code blocks; the code inside code blocks will be run during the testing phase
Use just f (alias for just format) to format Rust and TOML files before committing

Files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
🧠 Learnings (16)
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/tests/specs/**/*.{js,ts,tsx,jsx,json,css} : Test rules using snapshot tests via the `insta` library with test cases in `tests/specs/<group>/<rule_name>/` directories prefixed by `invalid` or `valid`

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useExplicitType/valid.ts
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `declare_node_union!` macro to query multiple node types at once by joining them into an enum with `Any*Like` naming convention

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `useValid` prefix for rules that report code that always evaluates to a constant (e.g., `useValidTypeof`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Deprecate rules by adding a `deprecated` field to the `declare_lint_rule!` macro with a message explaining the reason for deprecation (e.g., 'Use the rule noAnotherVar')

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/*.rs : Distinguish between `TypeData::Unknown` and `TypeData::UnknownKeyword` to measure inference effectiveness versus explicit user-provided unknown types

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUnsafe` prefix for rules that report code leading to runtime failures (e.g., `noUnsafeOptionalChaining`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/*.rs : Use `TypeReference` variants (`Qualifier`, `Resolved`, `Import`, `Unknown`) to represent different phases of type resolution

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUndeclared` prefix for rules that report undefined entities (e.g., `noUndeclaredVariables`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/*.rs : Use `TypeReference` instead of `Arc` for types that reference other types to avoid stale cache issues when modules are replaced

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `useConsistent` prefix for rules that ensure consistency across the codebase (e.g., `useConsistentArrayType`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUseless` prefix for rules that report unnecessary code that could be removed or simplified (e.g., `noUselessConstructor`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `ConditionalParsedSyntax` for syntax that is only valid in specific contexts (e.g., strict mode, file types, language versions) and call `or_invalid_to_bogus()` to convert to a bogus node if not supported

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use `Semantic<T>` query type instead of `Ast<T>` when a rule needs to access the semantic model for binding references and scope information

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use language tags in documentation code blocks (js, ts, tsx, json, css) and order properties consistently as: language, then `expect_diagnostic`, then options modifiers, then `ignore`, then `file=path`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-24T18:04:57.309Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_diagnostics/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:04:57.309Z
Learning: Applies to crates/biome_diagnostics/**/*.rs : Implement the Diagnostic trait on types, or use the #[derive(Diagnostic)] procedural macro to implement the trait. Configure category, severity, description, message, location, and tags using the #[diagnostic] attribute

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Set rule severity to `error` for correctness/security/a11y rules, `warn` for suspicious/performance rules, `info` for style/complexity rules, and `info` for actions

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_explicit_type.rs

@ematipico
Copy link
Member

ematipico commented Nov 30, 2025

How do the rule compare against the TypeScript eslint rule, after these changes?

@arnold095
Copy link
Author

@ematipico

  • Alignments with typescript-eslint:
  • Callbacks in function arguments are allowed (contextual typing)
  • Higher-order functions returning typed functions are allowed
  • Functions with as const assertions are allowed
  • IIFEs are allowed
  • Typed function expressions (via annotation or assertion) are allowed
  • Parameters with default values are now allowed

Biome-specific behavior:

  • Also checks module-level variable declarations
  • Allows trivially inferrable expressions: literals, new expressions, call expressions, binary/logical/comparison expressions, arrays, conditionals, and object literals

Regarding variable declaration checking:

This PR relaxes the rule significantly, but I'd like to open a discussion about whether useExplicitType should validate variable assignments at all.

The typescript-eslint rules (explicit-function-return-type, explicit-module-boundary-types) focus exclusively on function signatures. Variable type inference in TypeScript is robust and reliable and the compiler always knows the type.

The current implementation still flags some patterns where TypeScript can perfectly infer the type but requires semantic analysis to verify (e.g., enum members, variable references, class references).

Should we consider:

  • Removing variable checking entirely from this rule (align with typescript-eslint)
  • Creating a separate rule for variable type annotations if needed
  • Or keeping the current behavior with documented limitations?

I would appreciate your opinion on the intended scope of this rule.

@Lewenhaupt
Copy link

Would really love this. I think the requirement to have all variables defined is very hard (especially for zod objects).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Linter Area: linter L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

💅 useExplicitType is too strict

4 participants