X Tutup
The Wayback Machine - https://web.archive.org/web/20210324214648/https://github.com/microsoft/TypeScript/issues/20024
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow type annotation on catch clause variable #20024

Open
jaredru opened this issue Nov 15, 2017 · 59 comments
Open

Allow type annotation on catch clause variable #20024

jaredru opened this issue Nov 15, 2017 · 59 comments

Comments

@jaredru
Copy link

@jaredru jaredru commented Nov 15, 2017

TypeScript Version: 2.6.1

const rejected = Promise.reject(new Error());

async function tryCatch() {
  try {
    await rejected;
  } catch (err: Error) { // TS1196: Catch clause variable cannot have a type annotation
    // Typo, but `err` is `any`, so it results in runtime error
    console.log(err.mesage.length);
  }
}

function promiseCatch() {
  rejected.catch((err: Error) => { // OK
    // Compiler error; Yay!
    console.log(err.mesage.length);
  });
}

This was discussed in #8677 and #10000. It was closed as "fixed" in #9999, but as far as I can tell neither of the issues was actually resolved. In either event, I'd like to make a case for allowing type annotations in catch clauses.

Especially with the introduction of downlevel async functions, I'd suggest that disallowing catch clause type annotations leads to less safe code. In the example, the two methods of handling the promise are functionally equivalent, but one allows you to type the error, and the other doesn't. Without writing extra code for the try/catch version (if (err instanceof Error) { or const e: Error = err or something), you'll get a runtime error that you wouldn't get with the pure Promise version.

The primary rationale for not allowing this is that any object can be thrown, so it's not guaranteed to be correct. However, most of the benefit of TypeScript comes from making assertions about your and other people's code that can't be strictly guaranteed (especially when importing JavaScript). And unless one would argue that the Promise catch function also shouldn't allow a type annotation on the error parameter, this argument seems to make very little practical sense.

I believe one of the other arguments against is that it might be confusing, as it looks like the typed exception handling you might see in other languages (e.g., Java), and folks may think the catch will only catch errors of the annotated type. I don't personally believe that's a legitimate issue, but if it really is I'd propose at least allowing a catch (err as Error) { syntax or similar as a way of emphasizing that it's a type assertion.

If nothing else at all, it seems that there should be a way to trigger a warning (similar to an implicit any warning) when using an untyped err directly within a catch block.

@DanielRosenwasser
Copy link
Member

@DanielRosenwasser DanielRosenwasser commented Nov 15, 2017

I do like the idea of catch (err as Error) because, damn it, if you were going to cast it anyway, maybe we should make your life a little easier while keeping it explicit.

@yortus
Copy link
Contributor

@yortus yortus commented Nov 15, 2017

I agree, it is possible to safely annotate the error variable in many situations. Older discussion in #8677 (comment).

It also true that it's easy to misunderstand/abuse, but then so are type annotations in general, and people just do the cast in the first line of the catch block anyway.

@aluanhaddad
Copy link
Contributor

@aluanhaddad aluanhaddad commented Nov 20, 2017

@jaredru code like

function promiseCatch() {
  rejected.catch((err: Error) => { // OK
    // Compiler error; Yay!
    console.log(err.mesage.length);
  });
}

is dangerous and misleading. It only passes the type checker because the parameter of the catch method's callback is declared to be any. At best this is a disguised type assertion.

Promise.reject(NaN).catch((e: Error) => e.message.length);
@jaredru
Copy link
Author

@jaredru jaredru commented Nov 21, 2017

I disagree. It is no more dangerous or misleading than const foo: Foo = JSON.parse(input);, which is a very reasonable pattern. It represents our expectations and assertions about the code we're dealing with.

The irony of your example is that it throws a TypeError, which gets at my underlying point. Aside from contrived examples, the expected currency for errors is Error. A simple search through the TypeScript repo itself shows that catch (e) virtually always assumes the e to be an Error. That doesn't make it "right", of course, but it highlights the realistic expectation of a large, real-world project that (presumably) reflects an understanding of the balance between correctness and pragmatism, particularly as it relates to TypeScript.

@aluanhaddad
Copy link
Contributor

@aluanhaddad aluanhaddad commented Nov 21, 2017

I disagree. It is no more dangerous or misleading than const foo: Foo = JSON.parse(input);, which is a very reasonable pattern. It represents our expectations and assertions about the code we're dealing with.

I don't think that is a good pattern either.

const foo = <Foo>JSON.parse(input);

Is much clearer as to intent.

As for the example, contrived as it may be, it doesn't matter what gets thrown (TypeError or not) because it is thrown from the catch causing an unexpected failure.

@jaredru
Copy link
Author

@jaredru jaredru commented Nov 21, 2017

Sorry, consider it a typo. <Foo>JSON.parse(s) is a common pattern but is as unsafe as .catch((e: Error) => {. Both treat an any as another type with no guarantee of correctness beyond the developer's word.

it doesn't matter what gets thrown (TypeError or not) because it is thrown from the catch causing an unexpected failure.

Of course it matters. That the runtime throws an Error for all runtime errors strengthens the position that it's reasonable to expect the argument to catch to be an Error. This expectation is common in real world codebases--including, but certainly not limited to, TypeScript's.

@felixfbecker
Copy link

@felixfbecker felixfbecker commented Nov 28, 2017

My 2ct: If there is any party that should be able to type the exception it should be the called function, not the caller. I've seen codebases that throw strings everywhere or nothing at all (undefined). Just declaring that the type is Error doesn't make it more type safe, because you never know what other functions the function you are calling may call into and what things those nested functions throw - Errors are not strongly typed and not part of the interface contract. Maybe you know it now but in the next patch version of some transient dependency it could change. So the only correct way to mirror the runtime JS is to start with anyunknown and narrow it down manually

if (err instanceof MyError) ... else throw err

switch (err && err.code) {
  case 'ENOENT': ...
  case 'EPERM': ...
  default: throw err
}

const isExpectedHTTPError = (err: any): err is HTTPError => err && typeof err.status === 'number'

if (isExpectedHTTPError(err)) {
  res.status(err.status).set(err.headers || {}).send(err.body || '')
} else {
  res.status(500)
}

And if the check doesn't pass, rethrow the error. If you don't and your usage of the error doesn't directly produce some kind of TypeError then you might swallow unexpected programming errors and create a debugging nightmare.

Is this more code to write? Yes. But does that justify a new feature to make the unsafe version easier to write?

@jaredru
Copy link
Author

@jaredru jaredru commented Nov 28, 2017

My argument is for pragmatism. One of the stated non-goals of TypeScript's design is to "Apply a sound or 'provably correct' type system. Instead, strike a balance between correctness and productivity." Yes, I could get something besides an Error, but in reality--in the projects I'm working in--I won't.

As in the TypeScript codebase itself (1 2 3 4 5), it is very often safe and reasonable to assume that the thrown value is not, in fact, undefined (or null, or a string, ...) and is with almost certainty an Error. I would simply like the compiler to help me out in this case without additional code or runtime overhead.

@lordazzi
Copy link

@lordazzi lordazzi commented Jun 1, 2018

TypeScript language is already the best I've ever worked.
If you type the catch clause this will make much better.

@Andrew5569
Copy link

@Andrew5569 Andrew5569 commented Aug 31, 2018

I'd like to at least be able to do

  try {
    await rejected;
  } catch (err: unknown) { 
    // ...
  }
@felixfbecker
Copy link

@felixfbecker felixfbecker commented Aug 31, 2018

I would certainly love a exceptionTypeUnknown flag that makes all exception types (and Promise rejections) type unknown instead of any. That would definitely make the error handling safer as opposed to allowing any annotation. Although this could also be solved by writing a TSLint rule that forces them to always be declared unknown, if we had this annotation.

@bluelovers
Copy link
Contributor

@bluelovers bluelovers commented Sep 30, 2018

need this

@woodcockjosh
Copy link

@woodcockjosh woodcockjosh commented Jan 4, 2019

It's not productive for me to guess which properties are on the Error and have to look it up. Please implement.

@VincentLanglet
Copy link

@VincentLanglet VincentLanglet commented Feb 4, 2019

@DanielRosenwasser

I do like the idea of catch (err as Error) because, damn it, if you were going to cast it anyway, maybe we should make your life a little easier while keeping it explicit.

I think it would be awesome !

@Shinigami92
Copy link

@Shinigami92 Shinigami92 commented Feb 5, 2019

I like @Andrew5569 's Idea, but want to have the ability to define it like I want:

try {
  // ...
} catch (err: unknown) {
  // ...
}

try {
  // ...
} catch (err: Error) {
  // ...
}

try {
  // ...
} catch (err: AWS.AWSError) {
  // ...
}
@GabrielDelepine
Copy link

@GabrielDelepine GabrielDelepine commented Feb 7, 2019

@Shinigami92 It will never be possible. TS is being transcoded to Javascript for execution. And Javascript does not implement the type.

What would be possible is

try {
  // ...
} catch (err: unknown | Error | AWS.AWSError) {
  if (err instanceof Error) {
    // ...
  } else if (err instanceof AWS.AWSError) {
    // ...
  } else {
    // ...
  }
}
@Shinigami92
Copy link

@Shinigami92 Shinigami92 commented Feb 7, 2019

@GabrielDelepine sorry you get me wrong. I only want to assume the type, not to define it.

@thomas-darling
Copy link

@thomas-darling thomas-darling commented Feb 11, 2019

Yeah, please just fix this :-)


The fact that this is not allowed is both inconsistent and inconvenient:

try { ... } catch(error: Error) { ... }

Especially considering that this is allowed:

promise.catch((error: Error) => ... )

You can't argue that allowing it would be dangerous, just because people might incorrectly assume the type is Error, when it can in fact be anything. If someone were to assumes that, they will just cast to Error anyway, which would be just as much of an error. And this could be said for any type used anywhere, so there's really no valid reason to treat errors as a special case.

Not to mention, that in the vast majority of cases, assuming that the error will in fact be an Error, is actually perfectly reasonable. Please be a bit more pragmatic about those things, especially when it is something that gets in our way on a daily basis.


While you are at it, please also add an optional reject type to promises:

interface Promise<TResolve, TReject = any>

Because yes, we can, and often do, guarantee that an async function will always reject with a certain type - and arguing against that is kinda ridiculous. We can just wrap everything in the function in a try block, and throw a custom error if we end up in the catch.

For example, we have an ApiClient, which guarantees that if it rejects, it will always be with an ApiError. It's extremely annoying that we can't declare that fact in our types, but instead have to manually cast the errors everywhere we use it. That's neither practical, nor type safe.


These would not be breaking changes, they would provide a lot of value, and people have been asking for it for years - so please just implement it :-)

@jedmao
Copy link
Contributor

@jedmao jedmao commented Mar 13, 2019

I would love it if it were implemented with a new Throws<TReason = Error> type and Promise<TResolve, TReason> for Promises.

Ability to define and infer expected error types

function foo(): Throws<Error> { // should be inferred
    throw new Error()
}
try {
    foo()
} catch (error) { // <-- Error
    // noop
}

Throw something other than an Error (e.g., a number)

function bar(): Throws<number> { // should be inferred
    throw 42;
}
try {
    bar()
} catch (code) { // <-- number
    // noop
}

Multiple expressions within a try block

try {
    foo();
    bar();
} catch (reason) { // <-- Error | number
    // noop
}

Promise

async function baz(): Promise<never, TReason = Error> { // should be inferred
    return Promise.reject(new Error())
}
async function qux() {
    try {
        await baz()
    } catch (err) { // <-- Error
        // noop
    }
}

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code

As pointed out by @Shinigami92, it would be a breaking change unless implemented as Throws<TReason = any>, but that's not really a sensible default, as discussed above.

  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.
@Shinigami92
Copy link

@Shinigami92 Shinigami92 commented Mar 14, 2019

@jedmao

Why do you think your approach does not accept this?

function foo(): Throws<Error> { // should be inferred
    throw new Error()
}
try {
    const err: Throws<Error> = foo()
} catch (error) { // <-- Error
    // noop
}

I also think that Throws<TReason = Error> introduces a breaking change when it is inferred (and it should).
Throws<TReason = any> would not do this.

@jedmao
Copy link
Contributor

@jedmao jedmao commented Mar 14, 2019

@Shinigami92 good point on the breaking change.

I don't think your example is any different than the never type.

function foo(): never {
    throw new Error();
}
try {
    const err: never = foo()
} catch (error) { // <-- any (existing implementation)
    // noop
}
@bpasero
Copy link
Member

@bpasero bpasero commented Apr 24, 2019

+1 for having type-check flow through try/throw/catch code. This is driving me nuts currently for adopting async/await with try/catch where now I have no way of typing the error (with promises I could at least add a type).

@jpike88
Copy link

@jpike88 jpike88 commented Jul 3, 2019

At the very least, you should be allowed to use a type that extends the Error type, as is done with @types/mysql.

                try {
			// perform database fetch
		} catch (error: MysqlError) {
			if (error.code === 'ER_DUP_ENTRY') {
				return;
			} else {
				throw error;
			}
		}
JamieMagee added a commit to renovatebot/renovate that referenced this issue Jul 17, 2020
JamieMagee added a commit to renovatebot/renovate that referenced this issue Aug 5, 2020
@guidobouman
Copy link

@guidobouman guidobouman commented Aug 6, 2020

This is more-or-less addressed in #39015, which will land in 4.0. Not perfect, but better than nothing.

@woodcockjosh
Copy link

@woodcockjosh woodcockjosh commented Aug 6, 2020

@guidobouman I agree that #39015 is a small step towards this feature but in no way addresses it. That merge request is mostly does nothing to address #20024 IMO because it requires unknown or any. Even if that merge request did allow for types, it wouldn't solve this issue unless it caught only those types in the compiler.

Something like this is what we're looking for:

try {
  doSomethingA();
  doSomethingB();
  doSomethingC();
} catch (e) {
  if (typeof e === 'string') {
    console.error(e);
  } else if (typeof e === 'number') {
    console.error('example error message:', e);
  } else if (e instanceof Error) {
    console.error(e.message);
  } else {
    throw e;
  }
}
@cyraid
Copy link

@cyraid cyraid commented Aug 9, 2020

Right now it's a bit verbose. What if I want to catch some exceptions and have it to continue to throw if it's not the one I wanted?
Currently I have to:

  try {
    something();
  } catch (err) {
    if (err instanceof SomeError) {
      doSomethingElse();
    } else if (err instanceof AnotherError) {
      doAnotherThing();
    } else if (err instanceof YetAnotherError) {
      yetAnotherThing();
    } else {
      throw err;
    } // Else //
  } // Try, Catch //

What I wouldn't mind seeing is:

  try {
    something();
  } catch (err: SomeError) {
    doSomethingElse();
  } catch (err: AnotherError) {
    doAnotherThing();
  } catch (err: YetAnotherError) {
    yetAnotherThing();
  } // Try, Catch //

Which would just output what was above. Would save me some typing and be pretty convenient like in Java and other languages.

JamieMagee added a commit to renovatebot/renovate that referenced this issue Aug 10, 2020
@guidobouman
Copy link

@guidobouman guidobouman commented Sep 2, 2020

Multiple catches are not a pattern that is known to JavaScript. Feels weird to change the structure of the language.

@cyraid
Copy link

@cyraid cyraid commented Sep 2, 2020

Multiple catches are not a pattern that is known to JavaScript. Feels weird to change the structure of the language.

Neither is private/public constructor fields, or public/protected/private class fields, etc etc.. Need I go on?

@jedmao
Copy link
Contributor

@jedmao jedmao commented Sep 2, 2020

Neither is private/public constructor fields, or public/protected/private class fields, etc etc.. Need I go on?

Microsoft did those features in the earlier days of TypeScript, but not long after decided to more closely align with the ECMAScript specification, which they have done extremely well since. Their design goals are clear and they will not be introducing features like this that deviate from the specification.

@cyraid
Copy link

@cyraid cyraid commented Sep 2, 2020

The whole point of Typescript is to type check as a transpiler to JavaScript, which is why we're not using vanilla JavaScript.

If you feel a feature could benefit many others, but dismiss it because it "feels weird" but makes sense in other languages and is successful and many use those features, why do you raise your voice against it?

We use Typescript for the sugar that it provides. Decorators? Typed variables? Type checks? Generics? Function overloads? Don't forget the new variadic tuple types in typescript 4.

The point is, it's meant for a more Java or OOP programming that helps you have more typed and safe control over your code. It will always run JavaScript, that's a given (as I think they stated that as a mission goal).

But we shouldn't feel weird when using a language that transpiles into another language if there's differences.. there will always be differences, and that's good.. Typescript !== JavaScript.

@cyraid
Copy link

@cyraid cyraid commented Sep 2, 2020

but not long after decided to more closely align with the ECMAScript specification

Can you provide a source reference to this decision? I can't seem to find it.

I'd like to be a part of that discussion. That's why I'm asking where you found it.

@jedmao
Copy link
Contributor

@jedmao jedmao commented Sep 2, 2020

I don’t use TypeScript for the sugar it provides. The sugar is the very thing that is not in their design goals today. It’s more about confidence and type safety and all the type information disappears at compile time, as it should. Decorators are another one of the earlier features they implemented before ironing out the design goals, so it’s irrelevant to your argument.

@jedmao
Copy link
Contributor

@jedmao jedmao commented Sep 2, 2020

I don’t think you’re going to get any traction requesting to change the design goals, BTW.

@styfle
Copy link
Contributor

@styfle styfle commented Sep 2, 2020

Can you provide a source reference to this decision? I can't seem to find it.

See Goals here: https://github.com/microsoft/TypeScript/wiki/TypeScript-Design-Goals

@cyraid
Copy link

@cyraid cyraid commented Sep 2, 2020

Can you provide a source reference to this decision? I can't seem to find it.

See Goals here: https://github.com/microsoft/TypeScript/wiki/TypeScript-Design-Goals

How would the multiple catch clause go against the mission goals exactly?

Also, they're aligning "with current and future ECMAScript proposals", not with the specification itself. There's a difference.

And I'm not requesting to change the design goals.

And again, I was asking for the reference to the decision to do this, as was mentioned. The commenter said there was a decision and I only would like to know where it is. :)

@cyraid
Copy link

@cyraid cyraid commented Sep 2, 2020

they will not be introducing features like this that deviate from the specification.

Are you speaking on behalf of Microsoft? Are you on the Microsoft team? Who's specification? JavaScript? Well introduce types and immediately it's against JavaScript specification.. what's your point?

In fact, I think this "aligns" with goal #1 very nicely.. "1. Statically identify constructs that are likely to be errors."

By clarifying in more detail to exactly catch the different typed errors visually and cleaner, than using many if statements in one single catch block, and safely rethrow if none are found.. just like others are most likely used to coming from java and the like.

@jedmao
Copy link
Contributor

@jedmao jedmao commented Sep 2, 2020

I’ve been following the project since the beginning and I’ve seen and even started enough of these types of conversations to know where Microsoft stands on these points. I didn’t like it at first, but now I 100% get it and respect their design goals.

There are other ways to statically identify constructs that are likely to be errors without adding new JS syntax that requires not just throwing away type information at compile time, but also modifying the AST.

I think the best way to think about it is if you throw away all the type information, the JS that is left should ideally be runnable, which might also require shims, but runnable without modification to the AST.

@cyraid
Copy link

@cyraid cyraid commented Sep 2, 2020

Again, you seem to think this is against the design goals? Many changes in the language including types, change the AST (Abstract Syntax Tree). Have you ever worked on a compiler? I have (if anyone is curious I did a bit of work on the Free Pascal compiler).

I referenced what the JavaScript could would be here: #20024 (comment)

What the transpiler would emit. It's safe, simple, straight forward. Requires no shims, and does not somehow modify JavaScripts AST. :)

@jedmao
Copy link
Contributor

@jedmao jedmao commented Sep 2, 2020

The first non-goal states the following:

use the behavior of JavaScript and the intentions of program authors as a guide for what makes the most sense in the language.

If this were Python, your request here would make perfect sense, as Python already supports it like so:

try:
  print(x)
except NameError:
  print("Variable x is not defined")
except:
  print("Something else went wrong")

But this is not Python.


Non-goal #5 states the following:

emit different code based on the results of the type system. Instead, encourage programming patterns that do not require run-time metadata.

You can see the problem pretty quickly if you try to transpile your proposal into JS. My best attempt would be the following:

  try {
    something();
  } catch (err) {
    if (err istanceof SomeError) {
      doSomethingElse();
    } else if (err instanceof AnotherError) {
      doAnotherThing();
    } else if (err instanceof YetAnotherError) {
      yetAnotherThing();
    }
  }

Notice how this now "requires runtime metadata"; furthermore, what should happen if SomeError were not a class, but an interface? What then? TS is not going to be doing run-time type checking. Remember that it's not just errors that can be caught, as you can throw anything.

@jedmao
Copy link
Contributor

@jedmao jedmao commented Sep 2, 2020

Again, you seem to think this is against the design goals?

It's against the non-goals, as stated above. Also, you're looking for "sugar," which is not in the design goals.

Many changes in the language including types, change the AST (Abstract Syntax Tree).

Sure, but my point was "after the types have been removed."

Have you ever worked on a compiler?

I translated the entire PostCSS project into TypeScript, but I don't see how that's relevant. I've also used the TypeScript compiler API to manipulate and create AST, so I'm very aware of how it works.

What the transpiler would emit. It's safe, simple, straight forward. Requires no shims, and does not somehow modify JavaScripts AST. :)

It's only simple in your exact use case and only if all the types are classes or functions (instanceof-able). It also modifies the AST, for sure. I'm not sure why you think that it doesn't, as it has to translate catch clauses into separate if statements with instanceof checks. You are deleting the catch clause AST nodes and inserting new ones for the if statements.

@cyraid
Copy link

@cyraid cyraid commented Sep 2, 2020

Notice how this now "requires runtime metadata"

It requires no runtime metadata, as you can see. :)

I asked if you worked on a compiler because you mentioned it modified the AST, which most real typescript language changes do? Arrow functions in fact translate to "function ()" and surrounds by an enclosure if I'm not mistaken? Either way, features like that drastically change the emitted AST.. Like most of the features when they target ES5.

Anyway, I'm done debating on this. If others would like it, they can voice.

@jedmao
Copy link
Contributor

@jedmao jedmao commented Sep 2, 2020

Notice how this now "requires runtime metadata"

It requires no runtime metadata, as you can see. :)

It absolutely does. The instanceof checks are runtime checks. And there is no way to check these statements against an interface or type that is not a class or function.

I asked if you worked on a compiler because you mentioned it modified the AST, which most real typescript language changes do? Arrow functions in fact translate to "function ()" and surrounds by an enclosure if I'm not mistaken?

Only to support older targets, but you are mistaken if you are talking about compiling to a modern target. Aside from access modifiers and decorators, you should be able to compile ESNext TS into ESNext JS by simply removing the type nodes from the AST.

@DanielRosenwasser
Copy link
Member

@DanielRosenwasser DanielRosenwasser commented Sep 2, 2020

Have you ever worked on a compiler? I have (if anyone is curious I did a bit of work on the Free Pascal compiler).

Hey all, let's please be respectful in tone - this can come off as intimidating.

@cyraid Even if this is a syntactically-driven construct (i.e. only entities that resolve to both a value and a type are allowed in those catch clauses),

  • it is very confusing to reuse : EntityName for anything other than erasable type annotations that have no runtime behavior
  • it conflicts, or competes, with a potential future ECMAScript syntax.

On TypeScript, we intentionally aim not to add new constructs that have any sort of runtime behavior at all if they are not part of ECMAScript, and that stance has hardened over the years.

@guidobouman
Copy link

@guidobouman guidobouman commented Sep 2, 2020

Sorry, my answer was a bit short. This discussion has exploded a bit, but let me explain my initial reasoning: Sure, they (most typescript features) are additions, but not structurally. Most annotations can be removed without transpilation. I'm a fan of terser forms of expressing a goal. But not when it changes the mental flow of the language, and might possiby collide with the development of EcmaScript itself. That's also reflected in their design goals, particularly:

  1. Avoid adding expression-level syntax.

See #18500 (comment) as a reference.

@qkreltms
Copy link

@qkreltms qkreltms commented Sep 3, 2020

I wonder when will this issues be implemented?

@kabaluyot
Copy link

@kabaluyot kabaluyot commented Dec 11, 2020

For now, you can do this with type casting:

  /**
   * Add a new waitlist entry.
   *
   * @return  {Promise}
   */
  async addWaitlist(): Promise<void> {
    try {
      const response = await this.$waitlistRepository.AddWaitlist(
        'test41@test.com'
      )
      console.log(response.message)
    } catch (e) {
      const error = <ErrorAPIResponseInterface>e
      console.log('RESULT', error.message)
    }
  }

The error constant has intellisense-enabled for your API error custom response.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
X Tutup