Skip to content
Sarj Code Standards

no-contradictory-type-predicate

Keep explicit type predicates consistent with their primitive, array or record membership check.

Why

TypeScript trusts an explicit type predicate without verifying its implementation. A contradictory check can admit the wrong primitive, overclaim a closed literal or empty domain, accept null as a non-null object, or remove valid alternatives from the false branch.

Fix

Match the predicate to every value the check accepts and rejects. Check literal membership for a closed domain and exclude null when promising a non-null object. Use a boolean return for an intentional value filter; preserve required public and callback contracts.

Examples

Before — flagged Array membership alone does not prove text elements
src/guard.ts
function texts(value: unknown): value is string[] {
return Array.isArray(value);
}
After — preferred Establish the element domain at the boundary
src/guard.ts
function texts(value: unknown): value is string[] {
return (
Array.isArray(value) && value.every((item: unknown) => typeof item === "string")
);
}
Before — flagged A string check admits other strings
src/guard.ts
function ready(value: unknown): value is "ready" {
return typeof value === "string";
}
After — preferred A closed input supplies the literal evidence
src/guard.ts
function ready(value: "ready" | null): value is "ready" {
return typeof value === "string";
}
Before — flagged A typeof-object check also accepts null
src/guard.ts
function nonPrimitive(value: unknown): value is object {
return typeof value === "object";
}
After — preferred Exclude null and recognize non-primitive functions
src/guard.ts
function nonPrimitive(value: unknown): value is object {
return (typeof value === "object" && value !== null) || typeof value === "function";
}
Before — flagged Do not promise a different primitive
src/guard.ts
function isText(value: unknown): value is string {
return typeof value === "number";
}
After — preferred Agree with the checked primitive
src/guard.ts
function isText(value: unknown): value is string {
return typeof value === "string";
}
Before — flagged Numbers cannot belong to an empty target
src/guard.ts
function impossible(value: unknown): value is never {
return typeof value === "number";
}
After — preferred An empty target has no successful member
src/guard.ts
function impossible(value: unknown): value is never {
return false;
}
Before — flagged Object membership does not establish required fields
src/guard.ts
type Account = { id: string };
function account(value: unknown): value is Account {
return typeof value === "object" && value !== null;
}
After — preferred Establish fields before promising the record
src/guard.ts
type Account = { id: string };
function account(value: unknown): value is Account {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
typeof value.id === "string"
);
}

References