Bird
Raised Fist0
Typescriptprogramming~15 mins

Never type and unreachable code in Typescript - Deep Dive

Choose your learning style10 modes available
Overview - Never type and unreachable code
What is it?
In TypeScript, the 'never' type represents values that never occur. It is used to indicate code paths that cannot be reached or functions that never return normally, such as those that always throw errors or run infinite loops. Unreachable code is code that the program can never execute because the flow of control never reaches it. Understanding 'never' helps catch logical errors and improve code safety.
Why it matters
Without the 'never' type and awareness of unreachable code, programs can hide bugs where some parts of the code are never used or expected to run but actually do. This can cause unexpected behavior or crashes. Using 'never' helps developers and tools detect impossible situations early, making code more reliable and easier to maintain.
Where it fits
Before learning about 'never', you should understand basic TypeScript types and control flow like conditionals and functions. After this, you can explore advanced type features like type guards, exhaustive checks, and error handling patterns that rely on 'never' to ensure all cases are handled.
Mental Model
Core Idea
The 'never' type marks code or values that cannot happen, helping catch unreachable or impossible paths in your program.
Think of it like...
Imagine a locked room with no key and no windows; no one can ever enter it. The 'never' type is like that locked room—no value or code can ever get inside or happen there.
┌───────────────┐
│   Function    │
│   returns    ├─────┐
│   value?     │     │
└───────────────┘     │
       │ Yes          │ No
       ▼              ▼
  ┌───────────┐   ┌─────────────┐
  │ normal    │   │ never (no   │
  │ return    │   │ return)     │
  └───────────┘   └─────────────┘

Unreachable code lies beyond 'never' paths.
Build-Up - 7 Steps
1
FoundationBasic TypeScript Types Overview
🤔
Concept: Learn what types are in TypeScript and how they describe values.
TypeScript uses types like 'string', 'number', and 'boolean' to describe what kind of values variables can hold. For example, a variable declared as 'let name: string' can only hold text. Types help catch mistakes before running the program.
Result
You can declare variables with specific types and get errors if you assign wrong values.
Understanding basic types is essential because 'never' is a special type that fits into this system to describe impossible values.
2
FoundationControl Flow and Function Returns
🤔
Concept: Understand how functions return values or don't return at all.
Functions usually return a value or 'void' if they don't return anything. For example, 'function greet(): void' means it returns nothing. But some functions never finish normally, like those that throw errors or loop forever.
Result
You can write functions that either return values or never return normally.
Knowing how functions return helps us see where 'never' fits: it describes functions that never return.
3
IntermediateIntroducing the 'never' Type
🤔Before reading on: do you think 'never' means a value that can be anything or no value at all? Commit to your answer.
Concept: 'never' is a type that represents values that never occur, such as functions that never return or code that cannot be reached.
In TypeScript, 'never' means a value that cannot exist. For example, a function that always throws an error has return type 'never' because it never finishes normally. Also, if you have a variable of type 'never', you can never assign any value to it.
Result
Functions that throw or loop forever are typed as returning 'never', helping TypeScript understand your code better.
Understanding 'never' helps catch unreachable code and impossible cases, improving code safety.
4
IntermediateDetecting Unreachable Code
🤔Before reading on: do you think unreachable code is always a syntax error or just a logical issue? Commit to your answer.
Concept: Unreachable code is code that can never run because the program flow never reaches it, often after 'never' returning functions.
TypeScript can warn you if you write code after a 'never' function call, like after 'throw' or 'return' statements. For example: function fail() { throw new Error('fail'); console.log('This is unreachable'); // TypeScript warns here } This helps you clean up code that will never run.
Result
You get warnings about unreachable code, helping you avoid dead code and bugs.
Knowing unreachable code exists helps you write clearer, cleaner programs without hidden mistakes.
5
IntermediateUsing 'never' for Exhaustive Checks
🤔Before reading on: do you think 'never' can help ensure all cases in a switch are handled? Commit to your answer.
Concept: 'never' is used to make sure all possible cases in code like switch statements are covered, catching missing cases at compile time.
When you use a switch on a variable with limited options, you can add a 'default' case that assigns the variable to 'never'. If any case is missing, TypeScript will error. Example: function check(value: 'a' | 'b') { switch(value) { case 'a': return 1; case 'b': return 2; default: const _exhaustiveCheck: never = value; return _exhaustiveCheck; } } This pattern helps catch bugs when new cases are added but not handled.
Result
Your code becomes safer by ensuring all cases are handled explicitly.
Using 'never' for exhaustive checks prevents bugs from missing cases in your logic.
6
AdvancedFunctions That Never Return Normally
🤔Before reading on: do you think a function that throws an error can have a return type other than 'never'? Commit to your answer.
Concept: Functions that always throw or loop forever have return type 'never' because they never produce a normal result.
Examples: function throwError(message: string): never { throw new Error(message); } function infiniteLoop(): never { while(true) {} } These functions signal to TypeScript that code after calling them is unreachable.
Result
TypeScript understands these functions never return, enabling better code analysis.
Recognizing these functions helps avoid unreachable code and clarifies program flow.
7
ExpertSubtle 'never' Behavior in Union Types
🤔Before reading on: do you think 'never' disappears or stays in union types? Commit to your answer.
Concept: 'never' acts as an empty set in unions and intersections, disappearing silently but affecting type narrowing and inference.
In unions, 'never' is ignored: type A = string | never; // same as string But in intersections, it can cause the whole type to become 'never': type B = string & never; // results in never This subtlety affects how TypeScript narrows types and infers results, especially in complex conditional types.
Result
Understanding this helps avoid confusing type errors and write precise type definitions.
Knowing how 'never' behaves in unions and intersections prevents subtle bugs in advanced type manipulations.
Under the Hood
TypeScript treats 'never' as the bottom type in its type system, meaning it is a subtype of every other type but has no values. When the compiler sees code paths that cannot produce a value or finish normally, it assigns 'never' to those paths. This helps the compiler perform control flow analysis to detect unreachable code and enforce exhaustive checks.
Why designed this way?
The 'never' type was introduced to give developers a way to express impossible code paths explicitly. Before 'never', unreachable code and functions that never return were harder to detect, leading to bugs. The design follows type theory principles where 'never' is the empty set, making type checking more precise and safe.
┌─────────────────────────────┐
│        Type System          │
│                             │
│  ┌───────────────┐          │
│  │   any (top)   │          │
│  └──────┬────────┘          │
│         │                   │
│  ┌──────▼───────┐           │
│  │   string     │           │
│  │   number     │           │
│  │   boolean    │           │
│  └──────┬───────┘           │
│         │                   │
│  ┌──────▼───────┐           │
│  │    never     │ < bottom type
│  └──────────────┘           │
└─────────────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does 'never' mean a variable can hold any value? Commit to yes or no.
Common Belief:Some think 'never' means a variable can hold any value or is like 'any'.
Tap to reveal reality
Reality:'never' means the opposite: it represents no possible value at all.
Why it matters:Confusing 'never' with 'any' leads to wrong assumptions about what values variables can hold, causing type errors or missed bugs.
Quick: Is unreachable code always a syntax error? Commit to yes or no.
Common Belief:Many believe unreachable code causes syntax errors or prevents compilation.
Tap to reveal reality
Reality:Unreachable code is usually a logical issue; TypeScript warns but still compiles the code.
Why it matters:Ignoring unreachable code warnings can hide dead code or logic errors that confuse maintenance and cause bugs.
Quick: Can a function that throws sometimes have a return type other than 'never'? Commit to yes or no.
Common Belief:Some think functions that throw errors can have normal return types like 'void' or 'string'.
Tap to reveal reality
Reality:Functions that always throw must have return type 'never' to signal they never return normally.
Why it matters:Using wrong return types hides unreachable code and breaks type safety guarantees.
Quick: Does 'never' stay visible in union types? Commit to yes or no.
Common Belief:People often think 'never' remains in unions and affects the type.
Tap to reveal reality
Reality:'never' disappears silently in unions, acting like an empty set.
Why it matters:Misunderstanding this leads to confusion in complex type definitions and unexpected type errors.
Expert Zone
1
'never' is the bottom type, so it is assignable to every other type but no type is assignable to it except itself.
2
In conditional types, 'never' can cause entire branches to disappear, affecting type inference subtly.
3
Using 'never' in exhaustive checks relies on strict compiler options like 'strictNullChecks' to work correctly.
When NOT to use
'never' should not be used as a general-purpose type for variables or parameters. For unreachable code detection, rely on compiler warnings and code structure. For functions that may throw sometimes but also return, use union return types instead of 'never'.
Production Patterns
In real-world TypeScript projects, 'never' is commonly used in exhaustive switch-case checks to ensure all cases are handled. It is also used in error handling functions that always throw, and in complex type guards to narrow types safely. Linters and strict compiler settings help enforce correct use of 'never' and detect unreachable code.
Connections
Mathematical Set Theory
'never' corresponds to the empty set in set theory.
Understanding 'never' as the empty set helps grasp why it has no values and why it is a subtype of all types.
Logic and Proof Theory
'never' represents a contradiction or falsehood in logic.
Seeing 'never' as falsehood explains why code paths typed as 'never' cannot happen, similar to impossible proofs.
Dead Code Elimination in Compilers
Unreachable code detection is related to compiler optimization techniques that remove dead code.
Knowing how compilers detect unreachable code helps understand why 'never' is useful for signaling impossible paths.
Common Pitfalls
#1Ignoring unreachable code warnings and leaving dead code in the program.
Wrong approach:function example() { throw new Error('stop'); console.log('This runs'); // unreachable but ignored }
Correct approach:function example() { throw new Error('stop'); // Removed unreachable code }
Root cause:Not understanding that code after 'never' returning functions is unreachable leads to cluttered and confusing code.
#2Using 'never' as a type for variables that can hold values.
Wrong approach:let x: never = 5; // error: Type '5' is not assignable to type 'never'
Correct approach:let x: number = 5; // correct type for holding numbers
Root cause:Misunderstanding 'never' as a general type instead of a type with no values.
#3Not using 'never' in exhaustive switch checks, missing cases silently.
Wrong approach:function check(value: 'a' | 'b') { switch(value) { case 'a': return 1; // missing case 'b' } }
Correct approach:function check(value: 'a' | 'b') { switch(value) { case 'a': return 1; case 'b': return 2; default: const _exhaustiveCheck: never = value; return _exhaustiveCheck; } }
Root cause:Not leveraging 'never' for exhaustive checks allows bugs from unhandled cases.
Key Takeaways
'never' is a special TypeScript type that represents values that never occur, helping catch impossible code paths.
Functions that always throw errors or loop forever should have return type 'never' to signal they never return normally.
Unreachable code is code that can never run; TypeScript warns about it to help keep code clean and correct.
'never' is essential for exhaustive checks in switch statements, ensuring all cases are handled and preventing bugs.
Understanding 'never' deeply improves your ability to write safe, maintainable TypeScript code and catch subtle errors early.

Practice

(1/5)
1. What does the never type represent in TypeScript?
easy
A. A variable that can hold any type of value.
B. A type that represents optional values.
C. A function that returns null or undefined.
D. A function that never returns normally, like one that always throws an error or loops forever.

Solution

  1. Step 1: Understand the never type meaning

    The never type is used for functions or expressions that never complete normally, such as those that throw errors or run infinite loops.
  2. Step 2: Compare with other types

    Other options describe common types like any, null, or optional, which are different from never.
  3. Final Answer:

    A function that never returns normally, like one that always throws an error or loops forever. -> Option D
  4. Quick Check:

    never means no normal return [OK]
Hint: Never means function never finishes normally [OK]
Common Mistakes:
  • Confusing never with void or any
  • Thinking never means optional or nullable
  • Assuming never is a value type
2. Which of the following is the correct syntax for a function that returns never in TypeScript?
easy
A. function fail(): never { throw new Error('Fail'); }
B. function fail(): void { return; }
C. function fail(): never { return null; }
D. function fail(): string { throw new Error('Fail'); }

Solution

  1. Step 1: Check function return type and body

    A function returning never must not return normally. Throwing an error fits this.
  2. Step 2: Validate each option

    function fail(): never { throw new Error('Fail'); } throws an error, so it never returns normally. function fail(): void { return; } returns void normally. function fail(): never { return null; } tries to return null, which is invalid for never. function fail(): string { throw new Error('Fail'); } returns string but throws, so return type mismatches.
  3. Final Answer:

    function fail(): never { throw new Error('Fail'); } -> Option A
  4. Quick Check:

    Throw error matches never return [OK]
Hint: Throw error to return never type correctly [OK]
Common Mistakes:
  • Returning a value in a never function
  • Using void instead of never
  • Returning null or undefined in never
3. What will be the output of this TypeScript code?
function check(value: string | number) {
  switch (typeof value) {
    case 'string':
      return 'string';
    case 'number':
      return 'number';
    default:
      const _exhaustiveCheck: never = value;
      return _exhaustiveCheck;
  }
}
console.log(check(42));
medium
A. 'string'
B. 'number'
C. Compilation error due to unreachable code
D. Runtime error

Solution

  1. Step 1: Analyze the switch cases

    The function handles 'string' and 'number' types explicitly, so the default case is unreachable for these inputs.
  2. Step 2: Check the call and output

    Calling check(42) matches the 'number' case, so it returns 'number'. The default case with never is never reached.
  3. Final Answer:

    'number' -> Option B
  4. Quick Check:

    Handled all cases, returns 'number' [OK]
Hint: Check switch covers all types, default never runs [OK]
Common Mistakes:
  • Thinking default case runs for known types
  • Confusing never with runtime errors
  • Expecting compilation error for unreachable default
4. Identify the error in this TypeScript code snippet:
function assertNever(x: never): never {
  throw new Error('Unexpected value: ' + x);
}

function process(value: string | number) {
  if (typeof value === 'string') {
    return 'string';
  } else if (typeof value === 'number') {
    return 'number';
  } else {
    return assertNever(value);
  }
}
medium
A. Error because assertNever should return void not never.
B. Error because assertNever is called with a non-never type.
C. No error; code is correct and safe.
D. Error because value can never be string | number.

Solution

  1. Step 1: Understand the function purpose

    assertNever is used to catch unexpected values at compile time by requiring a never type.
  2. Step 2: Analyze the process function

    All possible types string and number are handled. The else branch is unreachable, so value is never there, making the call valid.
  3. Final Answer:

    No error; code is correct and safe. -> Option C
  4. Quick Check:

    Unreachable else means value is never [OK]
Hint: Else unreachable means value is never type [OK]
Common Mistakes:
  • Thinking assertNever is called with string or number
  • Confusing never return with void
  • Assuming else branch always runs
5. Given this TypeScript code, what is the best way to ensure all cases are handled and catch missing cases at compile time?
type Shape = { kind: 'circle', radius: number } | { kind: 'square', size: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    // Missing case for 'square'
  }
}
hard
A. Add a default case that calls a function accepting never to catch unhandled cases.
B. Ignore missing cases; TypeScript will warn automatically.
C. Add a default case that returns 0 without checks.
D. Use any type for shape to avoid errors.

Solution

  1. Step 1: Identify missing case problem

    The 'square' case is missing, so the switch is not exhaustive.
  2. Step 2: Use never type to catch missing cases

    Adding a default case that calls a function requiring never forces TypeScript to check exhaustiveness and report missing cases.
  3. Final Answer:

    Add a default case that calls a function accepting never to catch unhandled cases. -> Option A
  4. Quick Check:

    Use never in default to enforce exhaustiveness [OK]
Hint: Use default with never to catch missing cases [OK]
Common Mistakes:
  • Assuming TypeScript warns without default never
  • Returning default values silently
  • Using any to bypass type safety
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard