TS18046Semantic Error
Since TS 4.4

Fix TS18046: 'x' Is of Type 'unknown'

Learn why TypeScript throws TS18046 when accessing properties on catch clause variables or other unknown-typed values, and how to narrow them safely.

error TS18046: 'x' is of type 'unknown'

What This Error Means

TypeScript error TS18046 is closely related to TS2571 but was introduced in TypeScript 4.4 to provide clearer error messages specifically for catch clause variables and other contexts where a value is typed as unknown. Instead of the generic "Object is of type 'unknown'", TS18046 names the specific variable: 'e' is of type 'unknown'.

This error appears most often when strict mode is enabled, because TypeScript 4.4 added useUnknownInCatchVariables to the strict family. Before this change, catch clause variables were implicitly typed as any, which let you access .message and other properties without checking. Now TypeScript requires you to verify the type first.

This is a safety improvement. In JavaScript, you can throw anything — a string, a number, an object, or even undefined. Assuming the caught value is always an Error instance is not safe, and TS18046 protects you from that assumption.

Common Causes

1. Accessing .message on a catch clause variable

The most common trigger by far. You catch an error and immediately try to read its message property.

// ❌ Error: 'error' is of type 'unknown'. (TS18046)
async function loadUserProfile(userId: string) {
  try {
    const response = await fetch(`/api/users/${userId}`)
    return await response.json()
  } catch (error) {
    console.error(`Failed to load profile: ${error.message}`)
    throw error
  }
}
// ✅ Narrow with instanceof before accessing properties
async function loadUserProfile(userId: string) {
  try {
    const response = await fetch(`/api/users/${userId}`)
    return await response.json()
  } catch (error) {
    if (error instanceof Error) {
      console.error(`Failed to load profile: ${error.message}`)
    } else {
      console.error('Failed to load profile:', error)
    }
    throw error
  }
}

2. Rethrowing with additional context

When wrapping errors to add context, you need to narrow before accessing the original error's properties.

// ❌ Error: 'err' is of type 'unknown'. (TS18046)
async function saveOrder(order: Order) {
  try {
    await db.orders.create(order)
  } catch (err) {
    throw new Error(`Failed to save order ${order.id}: ${err.message}`)
  }
}
// ✅ Build a safe error message
async function saveOrder(order: Order) {
  try {
    await db.orders.create(order)
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err)
    throw new Error(`Failed to save order ${order.id}: ${message}`)
  }
}

3. Accessing custom properties on error subclasses

If you throw custom error classes with extra properties, you need to narrow to that specific class.

class ApiError extends Error {
  constructor(
    message: string,
    public statusCode: number,
    public endpoint: string
  ) {
    super(message)
    this.name = 'ApiError'
  }
}
 
// ❌ Error: 'error' is of type 'unknown'. (TS18046)
try {
  await callExternalApi()
} catch (error) {
  if (error.statusCode === 404) {
    return null
  }
  throw error
}
// ✅ Narrow to the specific error subclass
try {
  await callExternalApi()
} catch (error) {
  if (error instanceof ApiError && error.statusCode === 404) {
    return null
  }
  throw error
}

4. Logging errors in a utility function

Helper functions that accept caught errors need to handle the unknown type.

// ❌ Error: 'error' is of type 'unknown'. (TS18046)
function logError(context: string, error: unknown) {
  console.error(`[${context}] ${error.message}`)
  console.error(error.stack)
}
// ✅ Create a robust error logging utility
function logError(context: string, error: unknown) {
  if (error instanceof Error) {
    console.error(`[${context}] ${error.message}`)
    if (error.stack) {
      console.error(error.stack)
    }
  } else {
    console.error(`[${context}] Non-error thrown:`, error)
  }
}

How to Fix It

  1. Use instanceof Error — this is the go-to fix for catch blocks. It narrows the variable to the Error type, giving you access to message, stack, and name:
catch (error) {
  if (error instanceof Error) {
    console.log(error.message)
  }
}
  1. Create a helper function — if you handle errors in many places, extract the narrowing logic into a reusable utility:
function getErrorMessage(error: unknown): string {
  if (error instanceof Error) return error.message
  if (typeof error === 'string') return error
  return 'An unknown error occurred'
}
 
// Usage
catch (error) {
  console.error(getErrorMessage(error))
}
  1. Use String(error) for simple logging — if you only need a string representation and do not need specific properties:
catch (error) {
  console.error('Operation failed:', String(error))
}
  1. Avoid annotating catch variables as any — while catch (error: any) technically works in some configurations, it throws away type safety. Prefer narrowing instead.

  2. Handle non-Error throws — remember that JavaScript allows throwing any value. Your catch block should handle strings, numbers, and other non-Error values gracefully.

FAQ

What causes TypeScript error TS18046?

TS18046 occurs when you try to use a value that TypeScript has typed as unknown without narrowing it first. The most common scenario is the error variable in a catch block when useUnknownInCatchVariables is enabled (which is included in strict mode since TypeScript 4.4). TypeScript types catch variables as unknown because JavaScript allows throwing any value, not just Error instances.

How do I fix "'e' is of type unknown" in catch blocks?

Use an instanceof check to narrow the error variable before accessing its properties. Write if (error instanceof Error) to safely access error.message and error.stack. For simple logging, String(error) converts any thrown value to a string. If you handle errors in many places, create a helper function like getErrorMessage(error: unknown): string to centralize the narrowing logic.

How do I type catch clause errors?

You cannot add a type annotation to catch clause variables in TypeScript. Writing catch (error: Error) is a syntax error. The catch variable is always unknown (with strict mode) or any (without it). Instead, narrow the type inside the catch block body using instanceof, typeof, or custom type guards. This is intentional — since JavaScript can throw any value, TypeScript cannot guarantee the type at the declaration site.

Related Errors

Related Concepts

Share this reference

Stay Updated

Get the latest TypeScript tips, tutorials, and course updates delivered straight to your inbox.

No spam, unsubscribe at any time.