Shanmugaraja_K
Shanmugaraja_K

Reputation: 2382

How to do try catch and finally statements in TypeScript?

I have error in my project, and I need to handle this by using try, catch and finally.

I can use this in JavaScript but not in Typescript.

When I put Exception as argument in typescript catch statement, why it is not accepting this?

here is the code.

private handling(argument: string): string {
    try {
        result= this.markLibrary(argument);
    }
    catch(e:Exception){
        result = e.Message;
    }
    return result;
}

I need an exception message here but I can't get. And I got the below error.

Catch clause variable cannot have a type annotation.

Upvotes: 190

Views: 452462

Answers (9)

kmax
kmax

Reputation: 1

Using type as unknown should work

Use the below snippet as example

try {
  //your code
} catch (e: unknown) {
  if (e instanceof Error) {
    setErrorMessage(e.message)
  }
}  

Upvotes: 0

CyberT33N
CyberT33N

Reputation: 114

You should avoid to use type casting with as unless you 100% sure what you are expecting because you will overwrite the type of the error and you are not able to test it anymore or to pass it in the verified way.

This would be actually the right answer

import BaseError from './src/errors/BaseError'

const fn = () => {
    throw new Error('Any js error')
}

try {
    fn()
} catch (err) {
    const errMsg = 'Can not create chat completion'

    if (err instanceof Error) {
        throw new BaseError(errMsg, err)
    }

    throw new Error(errMsg, { cause: err })
}
  • Imagine you would use instead only throw new BaseError(errMsg, err as Error) then you would tell your custom error class that err is always an Error but this is risky and can cause problem the more specifc your error handling is or what you expect. E.g. when you expect AxiosError then you reached the catch block maybe with a normal js error instead but because you would write HttpClientError(errMsg, err as AxiosError) you would pass the normal error instance as AxiosError
  • The negative fact about this solution is that you would have to write 2 tests to get 100% coverage

Here is another example for a unit test:

it('should throw an error when initializing connection with mongoose fails', async () => {
    try {
        await mongooseUtils['init']()
        assert.fail('This line should not be reached')
    } catch (err) {
        if (err instanceof BaseError) {
            const typedErr = err 

            expect(typedErr.error?.message).toBe(expectedErrorMessage)
            expect(typedErr.message).toBe(
                '[ModelManager] - Error while initializing connection with MongoDB'
            )

            return
        }

        assert.fail('This line should not be reached')
    }
})

Here is an example for integration test with axios:

import axios, { AxiosError } from 'axios'
import { it, expect, assert } from 'vitest'
import { type IBaseError, StatusCodes } from 'error-manager-helper'

it('should return 500 with BaseError details - error passed', async() => {
    try {
        await axios.get('https://localhost:3000/base-error?param=wrong')
        assert.fail('This line should not be reached')
    } catch (err) {
        if (err instanceof AxiosError) {
            expect(err.status).to.equal(StatusCodes.INTERNAL_SERVER_ERROR)

            const data = err.response?.data as IBaseError
         
            expect(data.error).toEqual(`Error: ${errorMessageFromService}`)
            expect(data.message).toBe(errorMessage)

            return
        }

        assert.fail('This line should not be reached')
    }
})

Upvotes: 0

kani.euro
kani.euro

Reputation: 39

If you're using Axios and can't figure out the type of Error, you can do this:

import axios, { AxiosError } from "axios"
    
     try {
          const response = await get('https://url.com/users/')
          return response.data
        } catch (error) {
          throw new Error((error as AxiosError).message)
        }

Upvotes: 2

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249486

Edit

Typescript 4.0 added the ability to specify unknown and any on catch variables (Issue) And typescript 4.4 added the ability to make unknown the default on catch variables (PR) using the useUnknownInCatchVariables flag.

With this flag the following is now possible:

catch(e){
    result = e.message; // error under useUnknownInCatchVariables 
    if (typeof e === "string") {
        e.toUpperCase() // works, `e` narrowed to string
    } else if (e instanceof Error) {
        e.message // works, `e` narrowed to Error
    }
}

Specifying arbitrary types on catch variables is still not supported.

Original answer

Typescript does not support annotations on the catch variable. There is a proposal to allow this but it is still being discussed (see here).

Your only solution is to use a type assertion or an extra variable:

catch(_e){
    let e:Error= _e;
    result = e.message;
}

catch(e){
    result = (e as Error).message;
}

Unfortunately this will work as well and is completely unchecked:

catch(e){
    result = e.MessageUps;
}

Note

As you can read in the discussion on the proposal, in JS not everything that is thrown has to be an Error instance, so beware of this assumption.

Maybe tslint with no-unsafe-any would help catch this.

Upvotes: 187

MD Jahid Hasan
MD Jahid Hasan

Reputation: 1113

Can try this

try {...}
catch (e) {
    console.log((e as Error).message);
}

Also any type works as well.

try {...}
    catch (e:any) {
        console.log(e.message);
    }

But instanceof throw error.

try {...}
    catch (e) {
        console.log((e instanceof Error).message);
    }

Upvotes: 28

Masih Jahangiri
Masih Jahangiri

Reputation: 10897

Simple answer

catch (e) {
  const error = e as <Your custom error type>;
  ...
}

Upvotes: 3

Joyce Querubino
Joyce Querubino

Reputation: 84

What works in my case is like this. To print to terminal:

    catch(error){
      log('any_text', error as Error);
}

or call metod:

    catch(error){
       anything_here((error as Error).message),
}

Upvotes: 3

ford04
ford04

Reputation: 74490

With TypeScript 4.0, you can set unknown as catch clause variable type:

unknown is safer than any because it reminds us that we need to perform some sorts of type-checks before operating on our values. (docs)

try {  /* ... */ }
catch (e: unknown) { // <-- note `e` has explicit `unknown` type
    e.message // errors
    if (typeof e === "string") {
        e.toUpperCase() // works, `e` narrowed to string
    } else if (e instanceof Error) {
        e.message // works, `e` narrowed to Error
    }
    // ... handle other error types 
}

Playground

Update: TypeScript 4.4 provides a config flag --useUnknownInCatchVariables to let catch-variables default to type unknown. This is also automatically enabled with the --strict flag.

Upvotes: 118

Nguyen Phong Thien
Nguyen Phong Thien

Reputation: 3387

Firstly, you need to define the result variable

let result;

Secondly, you can't define the type of e - as the message said, so in case you want to force the type of e, use

catch(e){
    result = (e as Exception).Message;
}

or

catch(e){
    result = (<Exception>e).Message;
}

Otherwise, it should still work because e will have the type as any

catch (e) {
    result = e.Message;
}

Upvotes: 15

Related Questions