Current State
In packages/errors/src/is/index.ts:60-78, the is() function performs a null check on error at the top of the function, then re-checks typeof error === 'object' && error !== null ten lines later before accessing error[FACTORY_SYMBOL]. The compiler has already narrowed error to object; the second check is redundant and reads as the author having lost confidence in their own code.
// Line 60-63
if (error == null) {
return false;
}
// Line 77-78
if (typeof error === 'object' && error !== null) {
const marker = error as Record<typeof FACTORY_SYMBOL, unknown>;
Rule 0004 names this exact pattern in its "What this looks like in violation" section:
"A function declared with err: unknown that, three statements later, re-checks typeof err === 'object' && err !== null even though a prior branch already returned on err == null. The author was not sure the narrowing survived the intervening statements."
Located in:
packages/errors/src/is/index.ts:77-78
Problems with current implementation:
- The compiler has the narrowing. The re-check is a tax on every reader who has to confirm the second check is dead.
- It mirrors the canonical violation example in rule 0004.
- The pattern signals "the author didn't trust their own type narrowing", which propagates distrust to the next contributor.
Proposed State
After refactoring, the re-check is removed. The function accesses error[FACTORY_SYMBOL] directly after the top-level null guard:
if (error == null) {
return false;
}
// Native errors
if (typeof ErrorType === 'function' && 'prototype' in ErrorType) {
return error instanceof ErrorType;
}
// Factory instances
if (typeof error === 'object') {
const marker = error as Record<typeof FACTORY_SYMBOL, unknown>;
// ...
}
Expected improvements:
- The narrowing is trusted (rule 0004's trust-the-type principle).
- One fewer branch in the hot path.
- The reader can follow the narrowing without re-auditing the second check.
Motivation
This refactoring is needed because:
- It is the canonical violation example in rule 0004.
- The project slogan ("trust the type") is operationalised by trusting the narrowing; this code does the opposite.
- The redundant branch is noise that hides the actual logic of the function.
Triggers for this work:
Risks
Potential risks:
- Risk 1: TypeScript's narrowing across the
instanceof block may be lost, causing a new type error on error[FACTORY_SYMBOL]. — Mitigation: the compiler tracks narrowing across early-returns and type guards. If a regression appears, the fix is a single typeof error === 'object' cast at the access point, not the re-check.
- Risk 2: Consumer code paths where
error is null reach the second block. — Mitigation: the top-level if (error == null) return false; covers this. The re-check does not add safety.
Migration Plan
Migration approach:
- Remove the
typeof error === 'object' && error !== null wrapper.
- Run
pnpm --filter @deessejs/errors type-check to confirm the narrowing is preserved.
- Run the test suite to confirm runtime behaviour is unchanged.
Rollback plan: revert the PR. The change is local.
Backward Compatibility
Scope
Files/Folders affected:
packages/errors/src/is/index.ts (lines 77-78)
Out of scope:
Component(s) Affected
Note: the component_affected dropdown is calibrated for a web template project. The actual affected component is packages/errors.
Priority
Estimated Effort
Test Coverage Requirements
Testing Approach
Testing strategy:
- Unit tests: existing
is() test cases (factory instances, native errors, null/undefined) should all pass without modification.
Verification steps:
pnpm --filter @deessejs/errors test:run
pnpm --filter @deessejs/errors type-check
Related Issues / Pull Requests
Relevant Documentation
- Architecture doc:
docs/engineering/architecture/rules/0004-no-speculative-defences.md
Pre-Submission Checklist
Current State
In
packages/errors/src/is/index.ts:60-78, theis()function performs a null check onerrorat the top of the function, then re-checkstypeof error === 'object' && error !== nullten lines later before accessingerror[FACTORY_SYMBOL]. The compiler has already narrowederrortoobject; the second check is redundant and reads as the author having lost confidence in their own code.Rule 0004 names this exact pattern in its "What this looks like in violation" section:
Located in:
packages/errors/src/is/index.ts:77-78Problems with current implementation:
Proposed State
After refactoring, the re-check is removed. The function accesses
error[FACTORY_SYMBOL]directly after the top-level null guard:Expected improvements:
Motivation
This refactoring is needed because:
Triggers for this work:
packages/errors/src/against the 16 architecture rules, August 2026)Risks
Potential risks:
instanceofblock may be lost, causing a new type error onerror[FACTORY_SYMBOL]. — Mitigation: the compiler tracks narrowing across early-returns and type guards. If a regression appears, the fix is a singletypeof error === 'object'cast at the access point, not the re-check.errorisnullreach the second block. — Mitigation: the top-levelif (error == null) return false;covers this. The re-check does not add safety.Migration Plan
Migration approach:
typeof error === 'object' && error !== nullwrapper.pnpm --filter @deessejs/errors type-checkto confirm the narrowing is preserved.Rollback plan: revert the PR. The change is local.
Backward Compatibility
Scope
Files/Folders affected:
packages/errors/src/is/index.ts(lines 77-78)Out of scope:
catch {}).iindex; P0 chore: merge dev to main - core foundation complete #6 in spirit — registry pattern).Component(s) Affected
Note: the
component_affecteddropdown is calibrated for a web template project. The actual affected component ispackages/errors.Priority
Estimated Effort
Test Coverage Requirements
Testing Approach
Testing strategy:
is()test cases (factory instances, native errors, null/undefined) should all pass without modification.Verification steps:
pnpm --filter @deessejs/errors test:runpnpm --filter @deessejs/errors type-checkRelated Issues / Pull Requests
packages/errors/src/against rules 0001-0016, August 2026.docs/engineering/architecture/rules/0004-no-speculative-defences.md.Relevant Documentation
docs/engineering/architecture/rules/0004-no-speculative-defences.mdPre-Submission Checklist