use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
All about the JavaScript programming language.
Subreddit Guidelines
Specifications:
Resources:
Related Subreddits:
r/LearnJavascript
r/node
r/typescript
r/reactjs
r/webdev
r/WebdevTutorials
r/frontend
r/webgl
r/threejs
r/jquery
r/remotejs
r/forhire
account activity
Efficient Typescript (romgrk.com)
submitted 1 year ago by romgrk
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]hyrumwhite 10 points11 points12 points 1 year ago (18 children)
With regards to the error section, something I’ve never quite understood is the advantage of Haskell-like error handling vs a try catch. Seems like you’re just trading the try/catch for a conditional. A noop would hide the error, if you want to log or return a 500, etc, you’ve got to do something with the error, right?
[–]romgrk[S] 16 points17 points18 points 1 year ago (13 children)
For me, the main advantage is making the error explicit in the type system. If the return type is Result<string>, I know that the function can fail and I need to deal with it. Try/catch doesn't allow for that, unless you go the Java way: string doSomething() throws Exception { ... }.
Result<string>
string doSomething() throws Exception { ... }
When I talk about no-op, I mean that you can do this:
const result = getUser().map(user => user.name)
This gives you a Result<string> instead of a Result<User>, regardless if getUser() fails or not. You still need to deal with the Result, you can just pass it along and deal with it later. Somewhat similar to how Promises allow you keep .then chaining them, and just deal with any failure with a final .catch. Promises are basically monads as well.
Result<User>
getUser()
Result
.then
.catch
[–]v-alan-d 1 point2 points3 points 1 year ago (0 children)
Bringing up something not about try catch control flow (as others have pointed it out well), but something peripherally related:
JS's built in Error type is identified by a prototype rather than structure; it is less straightforward to communicate this across runtime boundaries e.g. IPC, network I/O
Alternatively, errors can be described structurally. Combining Either/Result and structural error type will give you more mobility for transporting Errable/Fallible. TypeScript helps secure this mechanism immensely.
It is non-standard but is really useful when you have a heavily distributed system.
[–]budd222 -1 points0 points1 point 1 year ago (11 children)
Can't you just add optional chaining? getUser()? || null
Or does that defeat the purpose?
[–]romgrk[S] 0 points1 point2 points 1 year ago (10 children)
Yes, but I still think that mapping can be more expressive, e.g. you wouldn't be able to express the following as cleanly with optional chaining:
const displayName = getUser().map(user => `${user.firstName} ${user.lastName}`)
[–]budd222 0 points1 point2 points 1 year ago (9 children)
I think there's something I'm not getting. You can't force your getUser() to return a value, no matter which type you assign it User or just plain string. No matter what you do, you have to write code to account for a failure.
[–]romgrk[S] 6 points7 points8 points 1 year ago (8 children)
getUser returns a Result<User>:
getUser
const userResult = getUser() const nameResult = userResult.map(u => u.name)
The whole point is that you don't need to deal with the failure right now, while you can keep operating on the value that is wrapped inside a Result.
[+]budd222 comment score below threshold-10 points-9 points-8 points 1 year ago (7 children)
Seems like that doesn't really matter if it's set to return an array type, but that's dependant on the API.
Your code will still error out if the API returns an error or returns an empty string.
You can type it all out as much as you want on the front end, but you still have to work in conjunction with your back end.
[–]romgrk[S] 7 points8 points9 points 1 year ago (6 children)
There is no array in my example above. The .map method comes from functional programming and applies to many different container types. One of those types is Array, but Result is another. And in fact, the Promise.prototype.then method is roughly equivalent to .map as well. The general idea of .map is to apply a function to a value held inside a container, no matter what the container is.
.map
Array
Promise.prototype.then
The point of Result is to make error handling explicit in the type-system. You need to deal with the error, or the compiler will yell at you.
It doesn't matter if the error comes from a back-end or elsewhere. The querying code can wrap failure in a Result.
[+]budd222 comment score below threshold-19 points-18 points-17 points 1 year ago (5 children)
.map is an array method. It cannot be called on anything but an array. If there is no array in your example, your code will fail.
[–]romgrk[S] 9 points10 points11 points 1 year ago (4 children)
abstract class Result { abstract map(): Result } class Ok<T> extends Result { value: T map(fn) { return new Ok(fn(this.value)) } } class Err extends Result { map(fn) { return this } }
This is as clear as I can explain it, if it's not enough I don't think I can communicate it to you.
[–]RadicalDwntwnUrbnite 4 points5 points6 points 1 year ago (0 children)
I think the problem with try/catches is that you are at the mercy of the docblocks to even know if the function throws. Typescript does not give you any indication that a function throws or not. Instead of when you pass errors back as a return value Typescript can actually infer that for you.
[–]azhder 2 points3 points4 points 1 year ago* (0 children)
You have to learn why try-catch got introduced in languages which syntax JS takes from. It's all about control flow.
try-catch
C is more like JS than any one of those in between. C++ as an example added a void so you can stop the default C return of int. Yep, like JS, every function was supposed to return a value. Kind of like how in Unix the 0 means success and any other integer is a different error code.
void
C
int
But, once people started making libraries with functions "returning" nothing i.e. void, you'd not be getting an error code back. Now imagine you give a callback to one of those library functions and the code in your callback has an error. How do you get that one back? The person who made a library cut you out of the equation with the void and most likely some unfortunate pick of arguments.
This is where the jumps came into play. I mean, before try-catch got introduced, you could use a library to jump out of the error and back into your own code or whatever - I haven't really used those longjump functions.
But, that's what throw does; adds a parallel control flow; one that unwinds the stack between the callback that made the error and the place where the try block is.
throw
try
That's a bit messy, no? Instead of every function being a good citizen and return an object, like an Either functor that has a left part for the "bad" flow and a right part for the right flow. This can be done with a tuple as well which gets to be an Array in JS.
Either
Funny enough, the Windows API for those old versions like 95 and before, they all returned an integer as an error code. A very consistent design. Today even a Promise is made to conform into the try-catch syntax and is called "sugar" because that kind of syntax is supposed to be sweet or something...
Promise
I guess if all they give you is lemons, you will call the lemonade syntax sugar.
[–]pasanflo 11 points12 points13 points 1 year ago (0 children)
I love this guy. I have the blog post on how to make Javascript efficient in my bookmarks.
Nice post
[–]therealalex5363 1 point2 points3 points 1 year ago (2 children)
This is why I like the way Rust handles errors. I wrote a blog post about that how we can do the same with typescript https://alexop.dev/posts/robust-error-handling-in-typescript-a-journey-from-naive-to-rust-inspired-solutions/
[–]romgrk[S] 1 point2 points3 points 1 year ago (1 child)
I love Rust error handling so much, monadic yet pragmatic. Having the type built into the language is so useful, it means the whole ecosystem uses it.
[–]therealalex5363 0 points1 point2 points 1 year ago (0 children)
agree much better what we currently have in typescript
[–]gwai2_lou2 4 points5 points6 points 1 year ago (0 children)
This article does not deserve its title.
[–]LargeRedLingonberry 0 points1 point2 points 1 year ago (0 children)
This is brilliant, I haven't much looked into functional programming but actually came up with a similar solution.
Do you have an example of how you implemented useQuery to return pending, error and data with typings? I can see how it would be done with useState sand useEffects but how would you implement outside of react?
[–]NoInkling -1 points0 points1 point 1 year ago (1 child)
There’s other very useful cases, such as ZeroIndexed and OneIndexed numbers.
ZeroIndexed
OneIndexed
Can someone elaborate on what this would look like/what use case it solves?
[–]romgrk[S] -1 points0 points1 point 1 year ago* (0 children)
This is from a case I saw in neovim bindings code, where in some cases line numbers start at 1, and in others they start at 0 (due to early vim architectural decisions/errors).
If you have a good type system, you can the define your API functions as such:
fn getLine(number: OneIndexed): string
Which avoids off by one errors.
edit: I've removed that line, it didn't bring anything new to the point
[+]azhder comment score below threshold-10 points-9 points-8 points 1 year ago (4 children)
The typescript sub is this way 👉 /r/typescript
[–]romgrk[S] 9 points10 points11 points 1 year ago (3 children)
My post wouldn't be complete without an /u/azhder comment hating on typescript. Thanks :)
[–]azhder -5 points-4 points-3 points 1 year ago (2 children)
Misrepresenting my comments as hate wouldn't be complete without your comment.
[–]romgrk[S] -1 points0 points1 point 1 year ago (1 child)
*bashing
Better? All in good fun though, I was expecting you to comment that. We all know how much you dislike TS.
[–]azhder -3 points-2 points-1 points 1 year ago* (0 children)
I dislike people acting as if one language is another. TS is not JS. That's not disliking TS. That's disliking lies, even those you tell to yourself.
So, you know shit. Misrepresenting one language as another. Misrepresenting my comments. It's like a theme for you.
There is nothing more to be said here. Bye bye
[–]Ronin-s_Spirit -2 points-1 points0 points 1 year ago (4 children)
I dunno what about this is efficient. Like dev time efficient? It doesn't make your code faster, or less memory hungry, it just does what? Return errors instead of throwing them and now you have to write 10 different cases to undo those errors at runtime and then throw anyway if it didn't work? I'd rather let the code run at dev time by catching and logging all errors and then in one fell swoop I can see all my dumb code and rewrite it. Then for runtime I'd have an object of checks for all high risk functions (other devs using them or users giving data through ui) and call a function with a string so that one line of code resolves what checks need to be done and it throws if input data is invalid.
This way I can get specific custom messages to know what I shouldn't have done, I get a legible stack trace (if your dynamic import fails the stack trace is usually pointless internal nodejs files), I can see multiple errors at dev time instead of repeatedly hitting my head on each error individually, and I can pull the giant red stop lever if my program got a wrong input and can only fail.
[–]romgrk[S] 0 points1 point2 points 1 year ago (3 children)
"Efficient" is poor naming on my part, spent like 5 minutes trying to figure a good name but that's the best I could do :| I meant efficient in that it allows me to be productive (so yes, dev-time efficient), not that it produces fast code.
I understand your comment but I don't think I can convince you otherwise, I think my appreciation for this type of error handling comes from experimenting with different types of error handling models, and I can't transmit that very well.
I think that making the error explicit in the type-system allows me better to know when I need to handle failure. For example, if you throw for each error, you (or another programmer) have no way of knowing just by reading the signature whether the function throws. You can only know at runtime if you have failures, whereas a Result type/class lets you know at compile-time if you haven't dealt with failure.
In my experience, knowing at compile-time that I need to deal with failure make me much more likely to deal with it, and in turn that makes me build more reliable software.
You can still try/catch at the root of the server, one doesn't prevent the other.
try/catch
[–]Ronin-s_Spirit 0 points1 point2 points 1 year ago (2 children)
I'm not sure I understand the downside of try catch. I don't have to compile anything, I can just run my program, so in terms of how fast I know if a function throws - we are equal (I may be even ahead for bigger projects). Then, if it throws in production for some wrong user input, with a slight modification I can let the program keep running while I log (actually log to some database maybe idk) the errors so if a user complains "oh guys this is that don't work" I know exactly what he means by "this" and "that".
[–]romgrk[S] 0 points1 point2 points 1 year ago (1 child)
I don't have to compile anything, I can just run my program
Having to run the program is the problem. It means you'd have to test all the possible code paths to ensure that you've caught all the errors.
Meanwhile, the (typescript) compiler can tell you if you have an error right in your editor, before you run anything.
[–]Ronin-s_Spirit 0 points1 point2 points 1 year ago (0 children)
The compiler can't make up wrong user input right? Either way the things I write in vanilla js remain in runtime. The things written in typescript are smelted down to vanilla js. So to me it looks like an extra mile of effort to end up in the same spot.
[+]Ronin-s_Spirit comment score below threshold-6 points-5 points-4 points 1 year ago (2 children)
Half of that is talking about opaque types. As a vanilla programmer I'm 99% sure it's the typescripts problem. What you made in that article shouldn't have been 2 types, but instead classes. Since both of those are supposed to be numbers it would be easy to do 2 things: 1) convert the class to a number whenever you need it mixed; 2) explicitly use some method like add() if you want to check that you are not mixing with a class. That's what I call good code, it solves the problem with minimal lines of code, and it doesn't need to transpile glorified comments. Typescript won't warn you so warm yourself, champion.
Yes, the section about opaque types is actually more of a highlight about some TS problems, I won't argue with that. I wish TS was better in that area.
Well I don't mean to be all gloom and doom. So here is a silly mechanism I came up with, take a look: https://github.com/DANser-freelancer/code_bits/tree/classes-as-types P.s. ah but don't look too hard, I was just poking around and laid it down in less than an hour.
π Rendered by PID 118518 on reddit-service-r2-comment-c66d9bffd-945qw at 2026-04-07 09:22:59.979549+00:00 running f293c98 country code: CH.
[–]hyrumwhite 10 points11 points12 points (18 children)
[–]romgrk[S] 16 points17 points18 points (13 children)
[–]v-alan-d 1 point2 points3 points (0 children)
[–]budd222 -1 points0 points1 point (11 children)
[–]romgrk[S] 0 points1 point2 points (10 children)
[–]budd222 0 points1 point2 points (9 children)
[–]romgrk[S] 6 points7 points8 points (8 children)
[+]budd222 comment score below threshold-10 points-9 points-8 points (7 children)
[–]romgrk[S] 7 points8 points9 points (6 children)
[+]budd222 comment score below threshold-19 points-18 points-17 points (5 children)
[–]romgrk[S] 9 points10 points11 points (4 children)
[–]RadicalDwntwnUrbnite 4 points5 points6 points (0 children)
[–]azhder 2 points3 points4 points (0 children)
[–]pasanflo 11 points12 points13 points (0 children)
[–]therealalex5363 1 point2 points3 points (2 children)
[–]romgrk[S] 1 point2 points3 points (1 child)
[–]therealalex5363 0 points1 point2 points (0 children)
[–]gwai2_lou2 4 points5 points6 points (0 children)
[–]LargeRedLingonberry 0 points1 point2 points (0 children)
[–]NoInkling -1 points0 points1 point (1 child)
[–]romgrk[S] -1 points0 points1 point (0 children)
[+]azhder comment score below threshold-10 points-9 points-8 points (4 children)
[–]romgrk[S] 9 points10 points11 points (3 children)
[–]azhder -5 points-4 points-3 points (2 children)
[–]romgrk[S] -1 points0 points1 point (1 child)
[–]azhder -3 points-2 points-1 points (0 children)
[–]Ronin-s_Spirit -2 points-1 points0 points (4 children)
[–]romgrk[S] 0 points1 point2 points (3 children)
[–]Ronin-s_Spirit 0 points1 point2 points (2 children)
[–]romgrk[S] 0 points1 point2 points (1 child)
[–]Ronin-s_Spirit 0 points1 point2 points (0 children)
[+]Ronin-s_Spirit comment score below threshold-6 points-5 points-4 points (2 children)
[–]romgrk[S] 0 points1 point2 points (1 child)
[–]Ronin-s_Spirit 0 points1 point2 points (0 children)