Skip to content

📎 noMisleadingReturnType: tracking remaining work #9810

@minseong0324

Description

@minseong0324

Description

Tracking remaining work for the noMisleadingReturnType nursery rule (#9799).
Opened per review feedback.

Rule-level improvements

  • Bare return / fall-through path detection - currently uses a conservative bail when the annotation includes undefined/void. This overlaps with type narrowing(📎 Implement type narrowing #8333); we should build on that infrastructure rather than implementing ad-hoc CFA in this rule.
  • Property-level as const in object literals - { a: "x" as const } and similar aren't detected because TypedService resolves the literal leaf to Unknown. The fix belongs in biome_js_type_info rather than ad-hoc AST walking in this rule.
  • Tuple element widening - is_nonunion_wider has no (Tuple, Tuple) arm, so [string, number] vs ["hello", 42] isn't detected.
  • Inferred union handling - when the annotation is non-union but the inferred type is a union (e.g. ternary b ? "a" : "b" infers "a" | "b"), is_wider_than doesn't decompose it.
  • Single-return bail-out for union annotations - the bail at line 254 skips when returns.len() == 1 && !has_any_const_return && is_literal_of_primitive, but this fires even when the annotation is a union like string | null. A function returning only "hello" with annotation string | null should be flagged since null is never returned. <no_misleading_return_type.rs>#L254

Known false positives

  • Short-circuit expressions with boolean operands - the type resolver leaves &&/|| results as Literal(false) | boolean instead of collapsing to boolean (TypeScript's canonical form), so the rule sees a union that looks wider than the annotation. Also affects ||, relational operators wrapped in &&/||, typeof x === "..." && ..., and instanceof-guarded chains. Sandbox
  • Mutable binding with reassignment - the type resolver wraps let bindings in a single-variant union containing only the initializer literal, ignoring reassignments. let n = 0; n = getNum(); return n; with : number is incorrectly flagged. Sandbox
  • Getter with matching setter - when a getter has a matching setter, TypeScript infers the getter's return type from the setter's parameter type instead of the body. The rule does not account for the setter and flags the getter based on its body literals alone.
  class Store {
    get status(): string { // it's correct! 
      if (Math.random() > 0.5) return "loading"; 
      return "idle"; 
    }
    set status(v: string) { }
  }
  • Nested object literal widening (depth > 1) - is_only_property_literal_widening only checks depth 1, so { inner: { flag: true } } vs { inner: { flag: boolean } } is flagged despite contextual widening. Sandbox
  • Union annotation with exhausted boolean variant - is_union_wider_than_returns reports the boolean variant as wider as soon as any return is true or false, without checking whether both are present. : boolean | null returning true, false, null is incorrectly flagged. Sandbox <no_misleading_return_type.rs>#L1104

Ideas for extending coverage

These are currently limited by type infra, with some possible directions:

These are just my initial thoughts - suggestions and alternative approaches are very welcome. I'd love to keep working on these over time, but would also be really grateful if anyone wants to pick any of them up.

Metadata

Metadata

Assignees

Labels

A-LinterArea: linterA-Type-InferenceArea: type inferenceL-JavaScriptLanguage: JavaScript and super languagesS-FeatureStatus: new feature to implement

Type

No fields configured for Task.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions