A function that both throws and returns an error-shaped object literal (a field named exactly error, ok, or success) for what looks like the same class of failure mixes two error-handling idioms. Callers can't compose the error path — some failures need a try/catch, others need an if (result.error) check.
function parseConfig(input: string) {
if (!input) {
throw new Error("input required");
}
const parsed = tryParse(input);
if (!parsed) {
return { error: "invalid config" };
}
return parsed;
}// fix — pick one idiom
function parseConfig(input: string) {
if (!input) {
return { error: "input required" };
}
const parsed = tryParse(input);
if (!parsed) {
return { error: "invalid config" };
}
return { ok: true, value: parsed };
}Not flagged — a function that only ever returns an error-shaped object, with no throw anywhere:
function loadResult(): Result {
return { error: null, value: 42 };
}Not flagged — a throw and an error-shaped return in the exact same block (the return is unreachable dead code, not a competing idiom):
function overlapping(x: number) {
if (x < 0) {
throw new Error("negative");
return { error: "unreachable" };
}
return x * 2;
}Not flagged — { error: null } and { ok: true } / { success: true } are the SUCCESS arm of a Result-shaped return, not a competing error idiom, even alongside an unrelated throw:
function divide(a: number, b: number) {
if (b === 0) {
throw new Error("division by zero is a programmer error");
}
return { error: null, value: a / b };
}Brace-less guard clauses (if (c) throw x;, no {}) are still treated as their own branch, so this flags the same way with or without braces:
function parseId(raw: string) {
if (!raw) throw new Error("id required");
if (raw.length > 64) return { error: "id too long" };
return raw;
}Scope: only inspects a function's own statements — nested functions (declarations, expressions, arrow functions) are separate scopes and are analyzed independently, not folded into the outer function's throw/return count. Arrow functions themselves aren't currently inspected as the outer subject of this check, only function declarations/expressions and class methods.