This TypeScript error indicates a logical condition that the type checker has determined will always evaluate to the same boolean value. This typically happens due to type narrowing, incompatible type comparisons, or redundant checks that make code unreachable.
TypeScript's type narrowing engine analyzes your code to determine the most specific type possible at any given location. When the compiler detects a condition that will always be true or false based on type information, it reports this error. This usually means either: 1. You're checking a condition that's already been narrowed to a different type 2. You're comparing incompatible types (like checking if a number equals a boolean) 3. You've already narrowed a value in a way that makes the condition impossible This is actually a helpful warningβit catches redundant or logically impossible code that could indicate a bug.
First, understand why TypeScript thinks the condition is always true/false. The error usually points to a logical inconsistency in your code.
Review the variable's type before the condition:
let value: string | null = getValue();
if (value === null) {
console.log("null");
} else {
// Here, value is narrowed to 'string' (not null)
if (value === null) { // ERROR: Always false - value can't be null here
console.log("unreachable");
}
}In this example, the second value === null check is always false because the else block already guarantees value is not null.
Delete or restructure the condition that will always be the same value.
Before:
function processString(str: string | null) {
if (str !== null) {
if (str === null) { // Always false - ERROR
console.log("Never reached");
}
}
}After:
function processString(str: string | null) {
if (str !== null) {
// str is now guaranteed to be a string
console.log(str.length); // No condition needed
}
}If you're comparing with a literal value, ensure the comparison makes logical sense.
Problem - comparing with wrong type:
const isActive: boolean = true;
if (isActive === "true") { // ERROR: boolean can't equal string "true"
console.log("always false");
}Solution - compare with correct type:
const isActive: boolean = true;
if (isActive === true) { // Correct, but usually just write: if (isActive)
console.log("active");
}Boolean comparisons to literal values are often redundant. Simplify them.
Before:
const isEnabled = checkFeature();
if (isEnabled === true) { // Redundant: always true when isEnabled is true
doSomething();
}After:
const isEnabled = checkFeature();
if (isEnabled) { // Cleaner and same logic
doSomething();
}If you have a union type, use explicit type guards to narrow safely.
Problem:
function processValue(value: string | number) {
if (typeof value === "string") {
// value is narrowed to 'string' here
if (typeof value === "number") { // ERROR: always false
console.log("unreachable");
}
}
}Solution - use proper type narrowing:
function processValue(value: string | number) {
if (typeof value === "string") {
console.log(value.toUpperCase()); // Can use string methods
} else {
console.log(value.toFixed(2)); // Can use number methods
}
}When strictNullChecks is enabled, null and undefined have distinct types. Verify your assumptions match.
Problem with strictNullChecks:
function getValue(): string {
return "hello";
}
const value = getValue(); // Type is 'string', never null/undefined
if (value === null) { // ERROR: Always false - string can't be null
console.log("unreachable");
}Solution - use proper optional types:
function getValue(): string | null {
return Math.random() > 0.5 ? "hello" : null;
}
const value = getValue(); // Type is 'string | null'
if (value === null) { // Now valid - value could be null
console.log("null case");
} else {
console.log(value.toUpperCase());
}### Disabling the Check (Not Recommended)
If you absolutely need to keep the always-true/false condition (rare), you can suppress the error:
// @ts-expect-error This condition is always true, but we need it for compatibility
if (legacyCondition === true) {
console.log("This might seem redundant but is needed for legacy reasons");
}However, this should be very rare. Most of the time, the error indicates actual logic bugs.
### TypeScript Configuration
The error checking is controlled by TypeScript's type checking. To see all such errors, ensure your tsconfig.json has strict mode enabled:
{
"compilerOptions": {
"strict": true,
"strictNullChecks": true,
"noImplicitAny": true
}
}### Related Issues in Type System
Similar "always true/false" patterns can occur with:
- Literal type narrowing
- Discriminated unions (where one property determines possible values)
- Type predicates (custom type guards)
In all cases, the fix is to update your conditions to align with the type system's understanding.
### Debugging Type Narrowing
If you're unsure what TypeScript thinks a type is at a specific point, use the "reveal type" technique:
function debug<T>(x: T): T {
type Check = typeof x; // Hover over 'Check' to see the type
return x;
}This helps you understand why a condition seems "always true/false" from the compiler's perspective.
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript