JSPM

  • Created
  • Published
  • Downloads 127813
  • Score
    100M100P100Q162646F
  • License Apache-2.0

Normalize exceptions/errors

Package Exports

  • normalize-exception

Readme

Codecov TypeScript Node Twitter Medium

Normalize:

Example

import normalizeException from 'normalize-exception'

try {
  throw 'message'
} catch (error) {
  console.log(error) // 'message'
  console.log(normalizeException(error)) // Error: message
  console.log(normalizeException(error) instanceof Error) // true
}

Install

npm install normalize-exception

This package is an ES module and must be loaded using an import or import() statement, not require().

API

normalizeException(error)

error any
Return value: Error

normalizeException() never throws.

If error is an Error instance, it is returned. Any missing or invalid error property is directly modified.

If it is not an Error instance, a new one is created and returned.

Features

Invalid type

Strings

try {
  throw 'message'
} catch (error) {
  console.log(error) // 'message'
  console.log(normalizeException(error)) // Error: message
  console.log(normalizeException(error) instanceof Error) // true
}

Plain objects

try {
  throw { name: 'TypeError', message: 'message' }
} catch (error) {
  console.log(normalizeException(error)) // TypeError: message
}

Others

try {
  throw null
} catch (error) {
  console.log(error.message) // Throws
  console.log(normalizeException(error).message) // 'null'
}

Missing properties

try {
  const error = new TypeError('message')
  delete error.name
  throw error
} catch (error) {
  console.log(error.name) // undefined
  console.log(normalizeException(error).name) // 'TypeError'
}

Mismatched name

try {
  const error = new TypeError('message')
  error.name = 'RangeError'
  throw error
} catch (error) {
  console.log(error.name) // 'RangeError'
  console.log(normalizeException(error).name) // 'TypeError'
}

Missing stack

try {
  const error = new Error('message')
  delete error.stack
  throw error
} catch (error) {
  console.log(error.stack) // undefined
  console.log(normalizeException(error).stack) // 'Error: message ...'
}

Invalid properties

try {
  const error = new Error('message')
  error.message = true
  throw error
} catch (error) {
  console.log(typeof error.message) // 'boolean'
  console.log(typeof normalizeException(error).message) // 'string'
}

Cached stack

try {
  throw new Error('message')
} catch (error) {
  console.log(error.stack) // Error: message
  error.message += ' other' // `error.stack` is cached, so it does not update
  console.log(error.stack) // Error: message
  console.log(normalizeException(error).stack) // Error: message other
}

Enumerable properties

class ExampleError extends Error {
  constructor(...args) {
    super(...args)
    // Common mistake: this makes `error.name` enumerable
    this.name = 'ExampleError'
  }
}

try {
  throw new ExampleError('message')
} catch (error) {
  console.log({ ...error }) // { name: 'Error' }
  console.log({ ...normalizeException(error) }) // {}
}

Readonly properties

try {
  const error = new Error('message')
  Object.defineProperty(error, 'message', { get: () => 'message' })
  throw error
} catch (error) {
  error.message = 'other' // Throws
  normalizeException(error).message = 'other' // Does not throw
}

Non-writable properties

try {
  const error = new Error('message')
  Object.defineProperty(error, 'message', { value: '', writable: false })
  throw error
} catch (error) {
  error.message = 'other' // Throws
  normalizeException(error).message = 'other' // Does not throw
}

Non-configurable properties

try {
  const error = new Error('message')
  Object.defineProperty(error, 'message', { value: '', configurable: false })
  throw error
} catch (error) {
  delete error.message // Throws
  delete normalizeException(error).message // Does not throw
}

Non-extensible error

try {
  const error = new Error('message')
  Object.preventExtensions(error)
  throw error
} catch (error) {
  error.prop = true // Throws
  normalizeException(error).prop = true // Does not throw
}

Proxies

try {
  throw new Proxy(new Error('message'), {})
} catch (error) {
  const { toString } = Object.prototype
  console.log(toString.call(error)) // '[object Object]'
  console.log(toString.call(normalizeException(error))) // '[object Error]'
}

Throwing properties

Proxies

try {
  throw new Proxy(new Error('message'), {
    get() {
      throw new Error('example')
    },
  })
} catch (error) {
  console.log(error.message) // Throws
  console.log(normalizeException(error).message) // Does not throw
}

Getters

try {
  const error = new Error('message')
  Object.defineProperty(error, 'message', {
    get() {
      throw new Error('example')
    },
  })
  throw error
} catch (error) {
  console.log(error.message) // Throws
  console.log(normalizeException(error).message) // Does not throw
}

Recursion

error.cause

try {
  throw new Error('message', { cause: 'innerError' })
} catch (error) {
  console.log(error.cause instanceof Error) // false
  console.log(normalizeException(error).cause instanceof Error) // true
}

error.errors

try {
  throw new AggregateError(['innerError'], 'message')
} catch (error) {
  console.log(error.errors[0] instanceof Error) // false
  console.log(normalizeException(error).errors[0] instanceof Error) // true
}

Related projects

Support

For any question, don't hesitate to submit an issue on GitHub.

Everyone is welcome regardless of personal background. We enforce a Code of conduct in order to promote a positive and inclusive environment.

Contributing

This project was made with ❤️. The simplest way to give back is by starring and sharing it online.

If the documentation is unclear or has a typo, please click on the page's Edit button (pencil icon) and suggest a correction.

If you would like to help us fix a bug or add a new feature, please check our guidelines. Pull requests are welcome!