34 Replies
You don't have fully tracked errors with this. You just know that some error may happen
You return the reason where you had your failure
The approach that leverages the type system the most and allows for things like exhaustive matching against literals is to have tracking of each type of error that might happen
Let's see if someone else has had to deal with this. I'm not using TypeScript and generally rely on
throw
when problems happen.This just says that its some error. Forgot to handle a specific one in a specific way? The type system will think its all fine.
While if you had tracking of each one, you would be forced to handle it
There are many other benefits
You could wrap each call in the try catch and explicitly create a unique reason that gets returned
Instead of throwing, you return a json object with reason field
Yeah but then I lose inference
And I need to type it by hand in a separate place, every time
Yes, if you want that level of control.
Let's see if one of the TypeScript developers here knows better.
Again, the point is not having to go through all of this
It could just be a function call that does the rollback
transactionSync()
isn't an option? if so, why not?
I think unless you are calling fetch()
or using a dependency that enforces async operations, you can probably use transactionSync()
If it's all SQLite, you may be good@Burrito better to move here
Sure. Which part are you having trouble with?
I think there is a way to do it, but I'm not sure if it will end up having some limitations, specially with RPC
I've given you a way.
By having your code use error as value paradigm, and then do your own throw at one single point.
You get fully type safe error.
Oh yeah
The thing is, I have to pass the error value as the
Error
messageNo you don't.
So that its available at runtime and i can check against it
The error doesn't even have to be an
Error
.
Okay just to get us on the same page, do you understand what the error as value paradigm is?...
"An exception thrown by an RPC method implementation will propagate to the caller. If it is one of the standard JavaScript Error types, the message and prototype's name will be retained, though the stack trace is not."
Yes but that's not the point, throwing is just a mechanism to let
transactionSync
rollback, you won't be using the thrown error as the mechanism to pass the type safe error back.Yeah
Because as you already know, the
catch (error)
has no type information, that's not what we will be using to pass the error back.Yup, I was thinking about some other case
But you are right
I can do it with a wrapper
Still would be nice to not need it though
It's very simple, you first decide on how you do want to encode your success and error in the error as value paradigm.
Yeah, its pretty straightforward
For the sake of the example, I'm going to encode success as
undefined
, and encode error as anything that is not undefined
.I already have some sample code working with it here hahaha
But its nice to leave it in chat
Then you can trivially write a type safe error transaction like:
That's it, now instead of calling
ctx.storage.transactionSync(...)
you just call safeTransactionSync
.
And all errors in the callback of safeTransactionSync
will be type safely propagated back as the return value.
If you have a different encoding of success and error, you just need to change the generic types and the line of error !== undefined
.Yeah that is definitely a nice solution for it
Again, would be nice to just have a function to rollback, specially as some people will end up not going through this just to get better types
I would agree, ideally the
transactionSync
callback would provide a rollback: () => never
as the first argument so you can call that, and () => never
will preserve TS CFA.
But that's only a viable solution if you assume people write throwless code in callback.
Otherwise people will effectively have to wrap try { ... } catch { rollback() }
in every single call.
Especially because storage.sql.exec
and friends can already throw.
So in practice, the current API design of throwing to rollback is the only practical one.Not really. I can treat the other errors as
unknown_error
by default when I do the catch that I will have to do anyway.
And I would have all my errors tracked the same way
Yes I would need to catch, but in both scenarios I need it
And throw could still rollback the same way it does nowMy point is that if you do an API design of:
Then for 99% of the people writing code, they have to write:
It's just unnecessary boilerplate, and on top of that if you did forget to call rollback, that transaction is doing nothing.
Why? Unhandled errors can have the same behavior as it has today.
Oh that's what you mean, yeah sure I can agree with that.
But you still have to decide how
rollback
behaves especially in regards to control flow.
Should rollback
throw or not? If rollback
throws, that means you still have to try
the entire transactionSync
call anyways.
If rollback
doesn't throw, then you are forcing people to write rollback(); return;
to stop rest of the code from executing, and that's also error prone boilerplate.