This TypeScript error (TS2367) occurs when you compare values of incompatible types that can never be equal. The comparison will always return false because the types have no common values. The fix involves checking your comparison logic, ensuring types match, or using type assertions if TypeScript's type narrowing is incomplete.
TypeScript error TS2367 warns you about comparisons that are guaranteed to produce the same result every time—always `true` or always `false`. This typically indicates a logic error in your code. The error occurs because TypeScript analyzes the types on both sides of a comparison operator (===, ==, !==, !=, <, >, etc.) and determines there's no overlap between them. For example: - Comparing a `number` to a `string` literal - Comparing a specific `enum` value to an impossible enum variant - Comparing a boolean to a different boolean literal - Comparing disjoint union types This error is a form of type safety—TypeScript is catching code that could never work as intended. It suggests either a typo, wrong variable, or flawed logic that should be fixed rather than ignored.
First, examine the exact line causing the error and understand what you're comparing:
// WRONG: Comparing number to string literal
let responseType = 'json'; // inferred as string
if (responseType === 'response') { // TS2367 error
// This never executes
}
// CORRECT: Either fix the string value or check the variable
if (responseType === 'json') {
// This executes as expected
}
// Or declare with more specific type
type ResponseType = 'json' | 'text' | 'blob';
let responseType: ResponseType = 'json';
if (responseType === 'json') {
// This works correctly
}Ask yourself:
- What type is the left side?
- What type is the right side?
- Do they overlap (share any possible values)?
Check if you've misspelled the value you're comparing against:
enum Status {
Active = 'active',
Inactive = 'inactive',
}
let status = Status.Active;
// WRONG: Typo—'active' doesn't exist in enum
if (status === 'activated') { // TS2367 error
// Never executes
}
// CORRECT: Use the actual enum value
if (status === Status.Active) {
// Works as intended
}
// Or use the correct string
if (status === 'active') {
// Also works if string literal is correct
}TypeScript is case-sensitive, so 'Active' and 'active' are different values.
Make sure you're comparing values of compatible types. Convert types if needed:
// WRONG: Comparing number and string
let age: number = 30;
if (age === '30') { // TS2367 error—number never equals string
// Never executes
}
// CORRECT: Convert to matching type
if (age === 30) {
// Works
}
// Or convert the string to a number
if (age === parseInt('30')) {
// Also works
}
// CORRECT: Use a union type if intentional
type AgeInput = number | string;
let userAge: AgeInput = 30;
if (typeof userAge === 'number' && userAge === 30) {
// Type guard narrows to number, comparison works
}Sometimes the issue is using the wrong logical operator:
// WRONG: Using && instead of || with impossible condition
if (status === 'active' && status === 'inactive') { // TS2367 error
// Never executes—status can't be both simultaneously
}
// CORRECT: Use || (OR) if checking for either value
if (status === 'active' || status === 'inactive') {
// Executes when status is either value
}
// CORRECT: Use separate conditions if checking multiple properties
if (status === 'active' && role === 'admin') {
// Executes when both conditions are true
}In some cases, TypeScript's type inference is too strict. Use type narrowing or assertions:
type ResponseType = 'json' | 'text' | 'blob';
// WRONG: TypeScript doesn't recognize reassignment in forEach
const types: ResponseType[] = ['json', 'text'];
types.forEach((type) => {
if (type === 'response') { // TS2367 error—not in union
// Never executes
}
});
// CORRECT: Use only values from the union
types.forEach((type) => {
if (type === 'json') {
// Executes for 'json' items
}
});
// CORRECT: Use type assertion if you're certain
const value = 'response' as ResponseType; // Forces the type
if (value === 'response') {
// Now acceptable (use with caution)
}Type assertions should be a last resort—they bypass TypeScript's safety checks. Prefer fixing the actual type or logic.
If TypeScript says a comparison will never be true, it's often dead code that should be removed:
// WRONG: Dead code—condition always false
const isDone = true;
if (isDone === false) { // TS2367 error—isDone is always true
console.log("Not done");
}
// CORRECT: Remove the impossible condition
const isDone = true;
if (!isDone) {
console.log("Not done");
}
// Or if you need to check at runtime, use a broader type
let isDone: boolean = true; // More flexible than literal true
if (!isDone) {
console.log("Not done");
}Use your IDE to identify and remove unreachable code paths. This improves code clarity and prevents bugs.
### Union Types and Type Narrowing
When working with union types, TypeScript requires type narrowing before comparisons:
type Result = { status: 'success'; data: string } | { status: 'error'; error: string };
let result: Result = { status: 'success', data: 'ok' };
// WRONG: TypeScript doesn't know which branch
if (result.status === 'pending') { // TS2367 error—not in union
// Never executes
}
// CORRECT: Check against possible values
if (result.status === 'success') {
console.log(result.data); // Now TypeScript knows data exists
}### Enum Comparisons
Enums in TypeScript have specific behavior:
enum Color {
Red = 'red',
Green = 'green',
Blue = 'blue',
}
let color = Color.Red;
// WRONG: Numeric comparison fails
if (color === 0) { // TS2367 error
// Never executes
}
// CORRECT: Compare to enum value
if (color === Color.Red) {
// Works
}
// Also correct: Compare to the string value
if (color === 'red') {
// Also works
}### Boolean Literal Types
Boolean values have literal types:
// WRONG: true is a literal, can't equal false
const isActive = true;
if (isActive === false) { // TS2367 error
// Never executes
}
// CORRECT: Use the correct boolean value or negation
if (isActive === true) { // or just: if (isActive)
// Executes
}
// CORRECT: Use a broader boolean type
let isActive: boolean = true; // Now can be true or false
if (isActive === false) {
// Might execute if isActive is later changed
}### Suppressing the Error (Not Recommended)
If you need to suppress this error (rare), use a type assertion:
// Suppress with 'as any' (not recommended—loses type safety)
if ((responseType as any) === 'response') {
// Compiler won't complain, but you've lost type checking
}However, suppressing the error is usually a sign you should fix the underlying logic instead. Use assertions sparingly and only when you have a good reason.
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