This error occurs when TypeScript infers a variable as the never type (representing impossible values) but you try to assign or use it where a string is expected, commonly from untyped empty arrays or over-narrowed type guards.
The "Type 'never' is not assignable to type 'string'" error indicates that TypeScript has determined a value should be of type `never` (a type representing values that never occur) but you're trying to use it in a context expecting a `string`. The `never` type in TypeScript represents the type of values that never occur. When TypeScript infers `never`, it's typically because type narrowing through control flow analysis has eliminated all possible types, or because an empty array was declared without explicit typing, leading TypeScript to infer `never[]`. This means the array can never contain any elements according to TypeScript's analysis. This error commonly appears when working with arrays, conditional type narrowing, or union types where TypeScript's type inference becomes overly restrictive and narrows a type down to an impossible state. The compiler is essentially telling you that based on your code's logic, it believes no valid value can exist in this position, yet you're trying to treat it as a string.
The most common cause is declaring an empty array without typing it. TypeScript infers never[] which cannot hold any elements.
Before (causes error):
const items = [];
items.push("hello"); // Error: Argument of type 'string' is not assignable to parameter of type 'never'After (fixed):
const items: string[] = [];
items.push("hello"); // OKFor React hooks:
const [items, setItems] = useState<string[]>([]);
setItems([...items, "hello"]); // OKIf type guards eliminate all possible types, TypeScript infers never. Check your conditional logic.
Problem code:
function processValue(value: string | number) {
if (typeof value === "string") {
return value.toUpperCase();
} else if (typeof value === "number") {
return value.toFixed(2);
}
// TypeScript infers 'value' is never here
const result: string = value; // Error: Type 'never' is not assignable to type 'string'
return result;
}Fix: Remove unreachable code or add proper type assertion:
function processValue(value: string | number): string {
if (typeof value === "string") {
return value.toUpperCase();
} else {
return value.toFixed(2);
}
// No unreachable code
}Generic functions like array methods may infer never when the type cannot be determined from context.
Problem:
const result = [].reduce((acc, item) => {
acc[item] = item; // Error: never type
return acc;
}, {});Fix with explicit types:
interface Result {
[key: string]: string;
}
const result = [].reduce<Result>((acc, item: string) => {
acc[item] = item;
return acc;
}, {});Or provide initial type:
const items: string[] = [];
const result = items.reduce((acc, item) => {
acc[item] = item;
return acc;
}, {} as Record<string, string>);If you know a value cannot actually be never at runtime, use type assertion as an escape hatch.
When TypeScript over-narrows:
function handleEvent(event: "click" | "hover") {
if (event === "click") {
// handle click
} else if (event === "hover") {
// handle hover
} else {
// TypeScript thinks this is unreachable (event is never)
// But if you're adding new event types, this might execute
const eventType: string = event as string; // Safe assertion
console.warn(`Unknown event: ${eventType}`);
}
}Caution: Only use assertions when you understand why TypeScript inferred never and you're certain the code is correct.
With discriminated unions, failing to handle a case can result in never inference.
Problem:
type Response =
| { status: "success"; data: string }
| { status: "error"; message: string };
function handleResponse(response: Response) {
if (response.status === "success") {
return response.data;
}
// Forgot to handle "error" case
const msg: string = response.message; // Error: Property 'message' does not exist on type 'never'
}Fix:
function handleResponse(response: Response): string {
if (response.status === "success") {
return response.data;
} else {
return response.message; // Now TypeScript knows this is the error case
}
}The TypeScript compiler option noImplicitAny helps prevent never type inference issues by requiring explicit types.
Update tsconfig.json:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}With noImplicitAny enabled, TypeScript will error on ambiguous empty arrays rather than silently inferring never[], forcing you to add explicit types:
// Will error without type annotation when noImplicitAny is true
const items: string[] = []; // Must explicitly typeThis makes type errors surface earlier in development rather than when you try to use the values.
Understanding never in the Type System
The never type is TypeScript's bottom type, meaning it's a subtype of every other type. Paradoxically, never is assignable to any type, but no type (except never itself) is assignable to never. This makes it useful for representing impossible states and exhaustiveness checking.
Empty Array Type Inference History
The inference of never[] for empty arrays has been debated in the TypeScript community (GitHub issues #18687, #20946, #51979). With strictNullChecks enabled, TypeScript cannot default to any[] or undefined[], so it chooses never[] to force developers to make explicit type decisions. While this prevents some bugs, it can be frustrating for beginners.
Library-Specific Issues
This error frequently appears in library contexts with complex generics:
- react-hook-form: useFieldArray can infer never for field names if types aren't properly constrained
- Redux: Action type inference sometimes resolves to never with incorrect reducer typing
- Styled-components: Variant props can infer never when extending component types
Exhaustiveness Checking Pattern
A common intentional use of never is for exhaustiveness checking in switch statements:
type Shape = Circle | Square | Triangle;
function area(shape: Shape): number {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2;
case "square": return shape.size ** 2;
case "triangle": return (shape.base * shape.height) / 2;
default:
const _exhaustive: never = shape; // Compile error if we add a new Shape
return _exhaustive;
}
}If you add a new shape type without handling it, TypeScript will error because the new type is not assignable to never.
Control Flow Analysis Limitations
TypeScript's control flow analysis is sophisticated but not perfect. It may not understand complex runtime logic, especially with:
- Indirect type guards through helper functions
- Complex boolean logic with multiple conditions
- Type predicates that don't narrow correctly
In these cases, explicit type annotations or assertions may be necessary even when the code is logically sound.
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