Skip to content

.isOk() / .isErr() / .isDefect() methods don't narrow (return boolean) #13

Description

@btravers

Summary

The instance methods .isOk() / .isErr() / .isDefect() return boolean and do not narrow. The standalone isOk(r) / isErr(r) / isDefect(r) functions do narrow. The dual API is a sharp, repeat-bite footgun — especially for codebases migrating from neverthrow, where the methods narrow.

Where

isOk(): boolean;
isErr(): boolean;
isDefect(): boolean;

vs the standalone guards:

declare function isOk<T, E>(r: Result<T, E>): r is OkView<T, E>;
declare function isErr<T, E>(r: Result<T, E>): r is ErrView<E, T>;
declare function isDefect<T, E>(r: Result<T, E>): r is DefectView<T, E>;

Why it matters

The natural, neverthrow-shaped pattern silently fails to compile away the other variants:

if (result.isErr()) {
  result.error; // ❌ not narrowed — `.error` not accessible
}

In a downstream consumer this is the single most-repeated mistake (it's documented as a "common mistake" in that repo's agent rules, and a code-review bot flagged a doc example that misused .isErr() as a guard). Anyone coming from neverthrow walks straight into it.

Proposal

Type the methods as type predicates so they narrow on their own:

isOk(): this is OkView<T, E>;
isErr(): this is ErrView<E, T>;
isDefect(): this is DefectView<T, E>;

This collapses the two parallel APIs into one and eliminates a whole class of mistakes. If there's a deliberate reason to keep the methods non-narrowing (e.g. steering users toward exhaustive match), documenting that rationale next to the standalone guards would at least make the split intentional rather than surprising.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions