How to fix Expression is not assignable to 'never' in TypeScript

TypeScriptINTERMEDIATEMEDIUMUpdated Dec 8, 2025· 136 views

This TypeScript error occurs when you try to assign a value to a variable or array that TypeScript has inferred as the 'never' type. This commonly happens with empty arrays, exhaustive type narrowing, or incorrect type annotations. The fix involves providing explicit type annotations or adjusting your code logic.

Quick fix

Fixes Expression is not assignable to 'never'

// BEFORE: Error - cannot push to never[]
const items = [];
items.push("hello"); // Error: Argument of type 'string' is not assignable to parameter of type 'never'

// AFTER: Explicitly type the array
const items: string[] = [];
items.push("hello"); // OK

// Or use initial value to help TypeScript infer
const items = ["initial"]; // Inferred as string[]
items.push("hello"); // OK

// For arrays of objects
const users: { name: string; id: number }[] = [];
users.push({ name: "Alice", id: 1 }); // OK

// Or with a type alias
type User = { name: string; id: number };
const users: User[] = [];
users.push({ name: "Alice", id: 1 }); // OK
  1. Provide explicit type annotation for empty arrays
  2. Fix over-narrowed union types
  3. Add type annotations to React state and props
  4. Review generic type constraints
typescript — error
// BEFORE: Error - cannot push to never[]
const items = [];
items.push("hello"); // Error: Argument of type 'string' is not assignable to parameter of type 'never'
// AFTER: Explicitly type the array
const items: string[] = [];
items.push("hello"); // OK
// Or use initial value to help TypeScript infer
const items = ["initial"]; // Inferred as string[]
items.push("hello"); // OK
// For arrays of objects
const users: { name: string; id: number }[] = [];
users.push({ name: "Alice", id: 1 }); // OK
// Or with a type alias
type User = { name: string; id: number };
const users: User[] = [];
users.push({ name: "Alice", id: 1 }); // OK
Expression is not assignable to 'never'

What this error means

The 'never' type in TypeScript represents values that never occur. It's the bottom type in TypeScript's type system—a type that is assignable to every other type, but no type is assignable to 'never' (except 'never' itself). When TypeScript infers a variable, parameter, or array as 'never', it means TypeScript has determined that no valid value should exist for it. This commonly happens in three scenarios: 1. **Empty arrays**: When you declare an empty array without an explicit type, TypeScript infers it as `never[]`, meaning an array that can contain nothing 2. **Exhaustive type narrowing**: When you've narrowed a union type down to the point where no possible values remain 3. **Impossible code paths**: When TypeScript determines a code branch should be unreachable based on type analysis The error message "Expression is not assignable to 'never'" indicates you're trying to assign a value to something TypeScript believes should never have a value. This is TypeScript's way of catching logical inconsistencies in your code.

Typical symptoms

  • Red squiggly line under array push operations or assignments in your IDE
  • TypeScript compilation fails with 'not assignable to never' error
  • Empty arrays cannot accept any values when you try to push to them
  • Variables that should accept values are typed as 'never' in IntelliSense
  • Error appears after exhaustive if/else or switch statements
  • Generic type parameters resolve to 'never' unexpectedly

Common causes

  1. Empty array declared without explicit type annotation (inferred as never[])
  2. Over-narrowing a union type until no valid types remain
  3. Conflicting type guards that eliminate all possible types
  4. Incorrect generic constraints that result in never
  5. Function return type inferred as never when all code paths throw or return never
  6. React state initialized as empty array without type annotation
  7. Object property types that contradict each other
  8. Using type assertions that create impossible type combinations

How to fix "Expression is not assignable to 'never'"

1Provide explicit type annotation for empty arrays

The most common cause is declaring an empty array without specifying its type. TypeScript infers it as never[], which cannot accept any values.

typescript
// BEFORE: Error - cannot push to never[]
const items = [];
items.push("hello"); // Error: Argument of type 'string' is not assignable to parameter of type 'never'

// AFTER: Explicitly type the array
const items: string[] = [];
items.push("hello"); // OK

// Or use initial value to help TypeScript infer
const items = ["initial"]; // Inferred as string[]
items.push("hello"); // OK

// For arrays of objects
const users: { name: string; id: number }[] = [];
users.push({ name: "Alice", id: 1 }); // OK

// Or with a type alias
type User = { name: string; id: number };
const users: User[] = [];
users.push({ name: "Alice", id: 1 }); // OK

This fix applies to React state, class properties, and any empty array declaration.

2Fix over-narrowed union types

When you narrow a union type too aggressively, TypeScript may eliminate all possibilities, resulting in 'never'.

typescript
// BEFORE: Over-narrowing creates 'never'
type Status = "pending" | "success" | "error";

function handleStatus(status: Status) {
  if (status !== "pending" && status !== "success" && status !== "error") {
    // TypeScript knows this is impossible - status is 'never' here
    console.log(status); // Type is 'never'
  }
}

// BETTER: Use exhaustive checks properly
function handleStatus(status: Status) {
  if (status === "pending") {
    console.log("Pending...");
  } else if (status === "success") {
    console.log("Success!");
  } else if (status === "error") {
    console.log("Error!");
  } else {
    // This should never execute - use for exhaustiveness checking
    const _exhaustive: never = status;
    throw new Error(`Unhandled status: ${_exhaustive}`);
  }
}

// Example with contradictory type guards
type Result = { success: true; data: string } | { success: false; error: string };

function process(result: Result) {
  // WRONG: This creates impossible condition
  if (result.success === true && result.success === false) {
    // result is 'never' here - impossible state
    console.log(result);
  }

  // CORRECT: Use proper narrowing
  if (result.success) {
    console.log(result.data); // TypeScript knows success=true case
  } else {
    console.log(result.error); // TypeScript knows success=false case
  }
}
3Add type annotations to React state and props

In React, untyped empty arrays in state or props frequently cause this error.

typescript
// BEFORE: React state inferred as never[]
import { useState } from "react";

function TodoList() {
  const [todos, setTodos] = useState([]);

  const addTodo = (text: string) => {
    setTodos([...todos, { id: Date.now(), text }]);
    // Error: Type '{ id: number; text: string; }' is not assignable to type 'never'
  };

  return <div>...</div>;
}

// AFTER: Explicitly type the state
import { useState } from "react";

type Todo = { id: number; text: string };

function TodoList() {
  const [todos, setTodos] = useState<Todo[]>([]);

  const addTodo = (text: string) => {
    setTodos([...todos, { id: Date.now(), text }]); // OK
  };

  return <div>...</div>;
}

// For complex state shapes
type AppState = {
  users: User[];
  posts: Post[];
  loading: boolean;
};

const [state, setState] = useState<AppState>({
  users: [],
  posts: [],
  loading: false,
});

This also applies to component props with array types.

4Review generic type constraints

Generic types with conflicting constraints can resolve to 'never'.

typescript
// BEFORE: Impossible generic constraint
type Impossible<T extends string & number> = T; // T is 'never' - nothing can be both string AND number

function process<T extends string & number>(value: T) {
  return value; // T is 'never' here
}

// AFTER: Use union instead of intersection when appropriate
type StringOrNumber<T extends string | number> = T; // OK - T can be string OR number

function process<T extends string | number>(value: T) {
  return value; // OK
}

// Example with array methods
const numbers = [1, 2, 3];
const filtered = numbers.filter((n) => n > 5); // May be empty, but still number[]

// If you further narrow incorrectly:
const items: (string | number)[] = ["a", 1];
const result = items.filter((x): x is string => typeof x === "string")
                     .filter((x): x is number => typeof x === "number");
// result is 'never[]' - nothing can be both string AND number
5Use type assertions carefully (last resort)

If TypeScript's inference is incorrect and you're certain about the type, you can use a type assertion. Use this sparingly.

typescript
// BEFORE: TypeScript incorrectly infers never
const data = JSON.parse(response); // Type is 'any'
const filtered = data.filter((x: any) => x.active); // Might be inferred as never[]

// AFTER: Assert the correct type
type User = { id: number; name: string; active: boolean };
const data = JSON.parse(response) as User[];
const filtered = data.filter((x) => x.active); // Type is User[]

// Alternative: Use type guard
function isUserArray(value: unknown): value is User[] {
  return Array.isArray(value) && value.every(
    (item) => typeof item === "object" &&
              "id" in item &&
              "name" in item
  );
}

const data = JSON.parse(response);
if (isUserArray(data)) {
  const filtered = data.filter((x) => x.active); // OK
}

Prefer proper type annotations over assertions when possible.

Advanced notes

### The 'never' Type in TypeScript's Type System

The 'never' type is TypeScript's bottom type, representing computations that never produce a value. It's used in:

1. Exhaustiveness checking:

typescript
type Action = { type: "increment" } | { type: "decrement" };

function reducer(state: number, action: Action): number {
  switch (action.type) {
    case "increment":
      return state + 1;
    case "decrement":
      return state - 1;
    default:
      // action is 'never' here if all cases are handled
      const _exhaustive: never = action;
      throw new Error(`Unhandled action: ${_exhaustive}`);
  }
}

If you later add a new action type but forget to handle it, TypeScript will error because the new type isn't assignable to 'never'.

2. Conditional types that can't be satisfied:

typescript
type IsString<T> = T extends string ? true : false;
type OnlyStrings<T> = T extends string ? T : never;

type Result1 = OnlyStrings<"hello">; // "hello"
type Result2 = OnlyStrings<42>; // never
type Result3 = OnlyStrings<string | number>; // string (never is filtered out in unions)

3. Functions that never return:

typescript
function throwError(message: string): never {
  throw new Error(message);
  // No return statement - execution never completes normally
}

function infiniteLoop(): never {
  while (true) {
    // Infinite loop - never returns
  }
}

### Empty Array Inference Deep Dive

TypeScript infers empty arrays as never[] by design—it's the only type that's safely assignable to any other array type:

typescript
// This works because never[] is assignable to string[]
const strings: string[] = [];

// If it were any[], this would compile but fail at runtime
const nums: number[] = ["oops"] as any[]; // Type system circumvented

The never[] inference is a safety feature that forces you to be explicit about what you intend to store.

### Union Type Distribution

In union types, 'never' is automatically filtered out:

typescript
type Example = string | never | number; // Simplifies to: string | number
type Filtered<T> = T extends string ? never : T;
type Result = Filtered<string | number | boolean>; // number | boolean

This makes 'never' useful for filtering types in mapped types and conditional types.

### React Hook Dependencies

typescript
// Common in useEffect/useCallback/useMemo
const [items, setItems] = useState([]); // never[]

useEffect(() => {
  // items is never[] - TypeScript won't autocomplete array methods properly
  console.log(items.length);
}, [items]);

// Better:
const [items, setItems] = useState<string[]>([]);

Frequently asked questions

What does "Expression is not assignable to 'never'" mean?
The 'never' type in TypeScript represents values that never occur. It's the bottom type in TypeScript's type system—a type that is assignable to every other type, but no type is assignable to 'never' (except 'never' itself). When TypeScript infers a variable, parameter, or array as 'never', it means TypeScript has determined that no valid value should exist for it.
How do I fix "Expression is not assignable to 'never'"?
This TypeScript error occurs when you try to assign a value to a variable or array that TypeScript has inferred as the 'never' type. This commonly happens with empty arrays, exhaustive type narrowing, or incorrect type annotations. The fix involves providing explicit type annotations or adjusting your code logic.
What causes "Expression is not assignable to 'never'"?
Empty array declared without explicit type annotation (inferred as never[]) Over-narrowing a union type until no valid types remain Conflicting type guards that eliminate all possible types
Is the "Expression is not assignable to 'never'" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading