marcXandre
marcXandre

Reputation: 2432

How to declare a function that throws an error in Typescript

In Java I would declare a function like this:

public boolean Test(boolean test) throws Exception {
  if (test == true)
    return false;
  throw new Exception();
}

And I can use this function without handling the exception.

If it is possible, how to do the same in Typescript? The compiler will tell me that I can't use the function without a try/catch.

Upvotes: 194

Views: 173087

Answers (10)

Klesun
Klesun

Reputation: 13719

You can mark the function with @throws jsdoc at least. Even though it does not provide static analysis errors in typescript compiler, some good IDE or linter may still report a warning if you try to disregard the function that throws...

/** 
 * @throws {Error}
 */
function someFunc() {
    if (Math.random() < 0.5) throw Error();
}
someFunc();

enter image description here

Upvotes: 45

Daniel Wasserlauf
Daniel Wasserlauf

Reputation: 88

Is not possible and likely won't be for a long time if ever. https://github.com/microsoft/TypeScript/issues/13219#issuecomment-1515037604 https://github.com/microsoft/TypeScript/issues/13219#issuecomment-1806338593 There was a link previous to the general github discussion, however the github discussion thread is lengthy and closed, so I wanted to save people the effort of reading the dicussion and just link the two most relevant comments.

I cannot comment on the relevant answer which linked to this thread otherwise I would.

For an even more TLDR: -TS team doesn't want to take the first step in normalizing typed errors since its not present in the community yet, and there doesn't exist any sort of typed error processing (like Java) within JS.

Upvotes: 2

snnsnn
snnsnn

Reputation: 13698

As indicated in other answers, in typescript, the return type of a fallible operation is never. There is no way to mark a function as throws however you can use a utility type to make it more discernible:

type Result<OK = any> = OK | never;

Or you can make it even more noticeable:

type Result<OK = any, Error = never> = OK | Error;

Again, these are for the eyes only, no way to enforce try/catch block.

If you want to force handling of errors, use a promise. Linters can cath unhandled promises. "typescript-eslint" has "No floating promises" rule.

https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-floating-promises.md

Also some runtimes emit errors when there is an unhandled promise.

Upvotes: 2

Toby Hobson
Toby Hobson

Reputation: 247

Coming from a functional background, I prefer to specify expected errors (aka checked exceptions) in the return type. Typescript unions and type guards make this simple:

class ValidationError {
  constructor(readonly message: string) {}

  static isInstance(err: unknown): err is ValidationError {
    if (err === undefined) return false
    if (typeof err !== 'object') return false
    if (err === null) return false
    return err instanceof ValidationError
  }
}

function toInt(num: string): number | ValidationError {
  const result = Number.parseInt(num)
  if (result === undefined) return new ValidationError(`Invalid integer ${num}`)
  return result
}

// caller
const result = toInt("a")
if (ValidationError.isInstance(result))
  console.log(result.message)
else
  console.log(`Success ${result}`)

This way, the function signature highlights the potential error to other developers. More importantly the IDE & transpiler will force developers to deal with them (in most cases). For example this will fail:

const result = toInt("a")
const doubled = result * 2

error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type

Upvotes: 4

Nico
Nico

Reputation: 917

This seems like a interesting PR to follow regarding this topic https://github.com/microsoft/TypeScript/pull/40468

This PR introduces:

  • A new type-level expression: throw type_expr. Currently throw type only throws when it is being instantiated.
  • A new intrinsic type TypeToString to print a type

Upvotes: 1

P Varga
P Varga

Reputation: 20269

Not TypeScript, but Hegel might be of interest which is another type-checker for JavaScript, and has this feature. You'd write:

function Test(test: boolean): boolean | $Throws<Exception> {
  if (test)
    return false;
  throw new Exception();
}

See https://hegel.js.org/docs/magic-types#throwserrortype

Upvotes: 1

lmiguelmh
lmiguelmh

Reputation: 3222

You could treat JavaScript's Error as Java's RuntimeException (unchecked exception). You can extend JavaScript's Error but you have to use Object.setPrototypeOf to restore the prototype chain because Error breaks it. The need for setPrototypeOf is explained in this answer too.

export class AppError extends Error {
    code: string;

    constructor(message?: string, code?: string) {
        super(message);  // 'Error' breaks prototype chain here
        Object.setPrototypeOf(this, new.target.prototype);  // restore prototype chain
        this.name = 'AppError';
        this.code = code;
    }
}

Upvotes: 2

Flavien Volken
Flavien Volken

Reputation: 21359

You cannot using pure ts (v<3.9) I hope it will be available in the future. A workaround is however possible, it consists of hiding the possible thrown types in the method's signature to then recover those types in the catch block. I made a package with this workaround here: https://www.npmjs.com/package/ts-throwable/v/latest

usage is more or less as follow:

import { throwable, getTypedError } from 'ts-throwable';
class CustomError extends Error { /*...*/ }

function brokenMethod(): number & throwable<CustomError> {
    if (Math.random() < 0.5) { return 42 };
    throw new CustomError("Boom!");
}

try {
    const answer: number = brokenMethod()
}
catch(error){
    // `typedError` is now an alias of `error` and typed as `CustomError` 
    const typedError = getTypedError(error, brokenMethod);
}

Upvotes: 1

sompnd
sompnd

Reputation: 651

It is not possible at this moment. You can check out this requested feature: https://github.com/microsoft/TypeScript/issues/13219

Upvotes: 27

Estus Flask
Estus Flask

Reputation: 223054

There is no such feature in TypeScript. It's possible to specify error type only if a function returns an error, not throws it (this rarely happens and is prone to be antipattern).

The only relevant type is never. It is applicable only if a function definitely throws an error, it cannot be more specific than that. It's a type as any other, it won't cause type error as long as it doesn't cause type problems:

function Test(): never => {
  throw new Error();
}

Test(); // won't cause type error
let test: boolean = Test(); // will cause type error

When there is a possibility for a function to return a value, never is absorbed by return type.

It's possible to specify it in function signature, but for reference only:

function Test(test: boolean): boolean | never {
  if (test === true)
    return false;

  throw new Error();
}

It can give a hint to a developer that unhandled error is possible (in case when this is unclear from function body), but this doesn't affect type checks and cannot force try..catch; the type of this function is considered (test: boolean) => boolean by typing system.

Upvotes: 200

Related Questions