How to fix Cannot access member 'x' for a type which is 'never' in TypeScript
This error occurs when TypeScript's type narrowing determines that a code path is unreachable (type 'never'), and you're attempting to access properties on it. The fix involves proper type guards and exhaustiveness checking.
Quick fix
Fixes Cannot access member 'x' for a type which is 'never'
type Status = 'pending' | 'success' | 'error';
function handleStatus(status: Status) {
if (status === 'pending') {
// handle pending
} else if (status === 'success') {
// handle success
}
// At this point, TypeScript knows status is 'never' if not all cases are handled
}- Identify the union type causing the issue
- Use exhaustiveness checking with never
- Handle all union cases explicitly
- Use type predicates for complex narrowing
type Status = 'pending' | 'success' | 'error';function handleStatus(status: Status) {if (status === 'pending') {// handle pending} else if (status === 'success') {// handle success}// At this point, TypeScript knows status is 'never' if not all cases are handled}Cannot access member 'x' for a type which is 'never'
What this error means
The 'never' type in TypeScript represents a type that has no values—an impossible state. When you see this error, it means TypeScript's control flow analysis has determined that your code should never reach that point. This commonly happens when: 1. You have a union type and forget to handle all possible cases 2. You have conditional type guards that don't cover all branches 3. TypeScript's type narrowing eliminates all possibilities before a certain line Accessing a property on 'never' is an error because logically, if a value can never exist, you can't access its properties. TypeScript is telling you something is wrong with your type logic, not your runtime code.
Typical symptoms
- TypeScript compiler error mentioning 'never' type
- Error message shows 'Cannot access member X for a type which is never'
- The error appears in code that should be valid
- Related to switch statements or conditional type checks
- Missing cases in union type handling
- Incomplete type guards in if/else chains
Common causes
- Unhandled union type cases (most common)
- Missing property in discriminated union type guard
- Type guard eliminates all possibilities in a branch
- Using arrow function with implicit 'never' return
- Exhaustiveness check failure with single-value unions
- Incorrect type narrowing with conditional types
- Missing case in switch statement with union types
How to fix "Cannot access member 'x' for a type which is 'never'"
First, locate the variable with type 'never'. Check what union type led to this:
type Status = 'pending' | 'success' | 'error';
function handleStatus(status: Status) {
if (status === 'pending') {
// handle pending
} else if (status === 'success') {
// handle success
}
// At this point, TypeScript knows status is 'never' if not all cases are handled
}Use your IDE's hover feature to see what type TypeScript infers at each point.
Create an exhaustiveness check function that catches unhandled cases:
function assertNever(value: never): never {
throw new Error(`Unhandled value: ${value}`);
}
type Status = 'pending' | 'success' | 'error';
function handleStatus(status: Status) {
switch (status) {
case 'pending':
console.log('Pending...');
break;
case 'success':
console.log('Success!');
break;
case 'error':
console.log('Error occurred');
break;
default:
assertNever(status); // TypeScript error if cases missing
}
}If you add a new status value to the union and forget to handle it in the switch, TypeScript will error at the assertNever call.
With if/else chains, ensure every case is covered:
type Response = { type: 'success'; data: string } | { type: 'error'; error: Error };
function handleResponse(response: Response) {
if (response.type === 'success') {
console.log(response.data);
} else if (response.type === 'error') {
console.log(response.error);
}
// All cases covered, no 'never' type here
}If you remove the else if block or don't handle all discriminator values, you'll get the 'never' error.
For complex type guards, define explicit predicates:
interface Dog { type: 'dog'; bark(): void }
interface Cat { type: 'cat'; meow(): void }
type Animal = Dog | Cat;
function isDog(animal: Animal): animal is Dog {
return animal.type === 'dog';
}
function isCat(animal: Animal): animal is Cat {
return animal.type === 'cat';
}
function interact(animal: Animal) {
if (isDog(animal)) {
animal.bark();
} else if (isCat(animal)) {
animal.meow();
}
// TypeScript knows all cases are covered
}In tsconfig.json, ensure strict mode is enabled:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
}
}Strict mode catches more type errors, including incomplete union handling.
Advanced notes
### Understanding 'never' in Type Narrowing
The 'never' type is the bottom type—assignable to every type, but no value is assignable to it. When TypeScript's control flow analysis exhausts all possible cases, the remaining type becomes 'never'.
type Literal = 'a' | 'b' | 'c';
function example(x: Literal) {
if (x === 'a') {
// x is 'a'
} else if (x === 'b') {
// x is 'b'
} else if (x === 'c') {
// x is 'c'
} else {
// x is 'never' — impossible to reach
}
}### Single-Value Union Edge Case
TypeScript has a known limitation: exhaustiveness checks work best with unions of 2+ members. Single-value unions sometimes don't trigger the 'never' type properly due to implicit any coercion.
Workaround: use a union of at least 2 types or force strict narrowing:
// Avoid:
type Single = 'only-value';
// Better:
type Single = 'only-value' | never;### Discriminated Unions (Recommended Pattern)
For complex types, use discriminant properties:
type Result<T> =
| { kind: 'success'; value: T }
| { kind: 'error'; error: string };
function process<T>(result: Result<T>) {
switch (result.kind) {
case 'success':
return result.value;
case 'error':
throw new Error(result.error);
default:
const _exhaustive: never = result;
return _exhaustive;
}
}TypeScript uses the kind field to narrow the type, eliminating 'never' errors when properly handled.
### Async/Promise Edge Cases
'never' can appear with promise rejection handling:
async function fetchUser(id: string): Promise<never> {
// This function never resolves or returns—it always throws or runs forever
throw new Error('Not found');
}Use Promise types for functions that might resolve.
Frequently asked questions
- What does "Cannot access member 'x' for a type which is 'never'" mean?
- The 'never' type in TypeScript represents a type that has no values—an impossible state. When you see this error, it means TypeScript's control flow analysis has determined that your code should never reach that point. This commonly happens when: 1.
- How do I fix "Cannot access member 'x' for a type which is 'never'"?
- This error occurs when TypeScript's type narrowing determines that a code path is unreachable (type 'never'), and you're attempting to access properties on it. The fix involves proper type guards and exhaustiveness checking.
- What causes "Cannot access member 'x' for a type which is 'never'"?
- Unhandled union type cases (most common) Missing property in discriminated union type guard Type guard eliminates all possibilities in a branch
- Is the "Cannot access member 'x' for a type which is 'never'" error serious?
- It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.