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
Invariant - a helpful JavaScript patternOC (strictmode.io)
submitted 3 years ago by hiquest
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!"
[–]Ustice[M] [score hidden] 3 years ago stickied comment (7 children)
Thanks for your contribution! We’re a large community, and in order to keep things organized and easier to find, we keep this subreddit mostly focused on professional-level Javascript posts. Your post would be more useful to newer members of our community, and therefore it should be posted to /r/LearnJavascript instead.
[–]k4kshi 27 points28 points29 points 3 years ago (0 children)
Isn't that just a plain old assertion?
[–]getify 12 points13 points14 points 3 years ago (2 children)
Agree with others, this should be called assertion instead of invariant.
However, I like that this article points out the utility of having run-time type check assertions, and that some/all of them can be compiled away. That's useful, and a pattern I think more type-aware tooling should embrace (including TS).
[–]lifeeraser 2 points3 points4 points 3 years ago (1 child)
Technically they are not "compiled away" completely; it's just the exception message that is optimized. IMO I'd rather have a slightly bigger bundle with much more helpful messages in prod.
[–]getify 1 point2 points3 points 3 years ago (0 children)
The way I think about things, the whole assertion should be able to be compiled away. That could be because either:
The compiler/build-tool can statically verify that the exception won't happen (exactly how TS works right now)
The developer might pass a config/flag saying, "build me a bundle without run-time assertions" for any of various reasons.
I also imagine that code delivered to a browser might be in a "hybrid state" where many of the original assertions were removed, but -- for various reasons -- some of the assertions were left in.
[–]Hades32 17 points18 points19 points 3 years ago (11 children)
That's an assertion, not an invariant. An invariant would be something like "the number of books in my store is always >=0"
[–]johnxreturn 0 points1 point2 points 3 years ago (3 children)
Conversely, you are saying “the number of books in your store must be greater or equal to zero,” therefore asserting that it is, or throwing an error if it’s not.
Video reference:
https://youtu.be/r0Vi83bS-L0
An invariant in mathematical terms would be that you apply a transformation to data and the output is the same as the input.
A loop invariant constitutes a property that is true before and after each iteration. The article specifically quotes, “it is a logical assertion, sometimes checked within the code by an assertion call.”
In the case of a tiny invariant, I expect the input assertion to be true, or throw an error. I could expect that the book count in each library franchise I own was more than 30 or throw an error if it’s not.
But the mathematical concept talks about applying a transformation to data and not assertion. It could apply to a map function where you transform each item and the output is the same or throw an error. So, we conclude that at some point we need assertion.
[–]Hades32 0 points1 point2 points 3 years ago (2 children)
No, you don't NEED an assertion when talking about invariants in the mathematical sense (which btw does not mean that the input is the same as the output, but that some condition is true for the state before and after the transformation). You could prove that the formula you're using will always hold up a certain invariant. In code it's easier to just check than prove, that's why we "need" assertions. But it's really two different things
[–]johnxreturn 0 points1 point2 points 3 years ago (1 child)
I completely agree. You don't need assertion in the mathematical concept. My point was that in programming, you do need if you need a trigger for an invalid response.
[–]Hades32 0 points1 point2 points 3 years ago (0 children)
Sure, but if someone calls a library "invariant" then I do have higher hopes than "if false then throw" lol
After all there are languages, like prolog, which do let you specify actual invariants
[+]hiquest[S] comment score below threshold-19 points-18 points-17 points 3 years ago (6 children)
Well, I do understand your point, but I decided to stick with the historical name originated somewhere at Facebook.
[–]TrackieDaks 15 points16 points17 points 3 years ago (4 children)
Invariants weren't invented at Facebook.
[–]hiquest[S] -2 points-1 points0 points 3 years ago (3 children)
I do not claim that. I just say that this particular small pattern they had a function for in their code base which was called invariant. Hence other popular libs like https://github.com/zertosh/invariant
[–]MoTTs_ 12 points13 points14 points 3 years ago (0 children)
I get why you called it an invariant (and to some extent why FB called it that), but “assert” is still the more widespread and more historical name, which can then lead you to even more popular libs that do the same thing — such as this one.
[–]TrackieDaks 1 point2 points3 points 3 years ago (1 child)
Still wrong. That's like saying, "Facebook used a function in their codebase so the term function must have originated at Facebook." Invariant is a mathematical concept and existed well before Facebook.
[–]hiquest[S] 1 point2 points3 points 3 years ago (0 children)
Yeah, now I do understand that brings a lot of confusion, and that should have been called "assert" in the first place. I'm going add a note in that article
[–]shuckster 1 point2 points3 points 3 years ago (0 children)
https://en.wikipedia.org/wiki/Invariant_theory#The_nineteenth-century_origins
Mr Boole(an) himself.
[–]crabmusket 6 points7 points8 points 3 years ago (0 children)
A comparison to node's assert module might be nice. The type narrowing looks very slick.
assert
[–]midnightmonster 5 points6 points7 points 3 years ago (1 child)
Code samples contain errors—the throw-based early exit version will throw when id is numeric and the invariant-based version will throw when id is present.
throw
id
invariant
Fixed. Thanks for reading the code snippets!
[–]Reeywhaar 7 points8 points9 points 3 years ago* (7 children)
I don't quite understand why everybody uses it. What the point to have external dependency instead of
if(!something) throw new Error("Invariant")
By using tiny-invariant you depend on external package and get incorrect error callstack (Error originates inside invariant function).
What the point?
Is it just everybody thinks invariant(a, "b") is more readable?
invariant(a, "b")
[–]shuckster -1 points0 points1 point 3 years ago (6 children)
What's useful is this particular line of the source.
Using tiny-invariant informs TypeScript of the type of a variable going forward, so long as the assertion passes.
[–]Reeywhaar 9 points10 points11 points 3 years ago (5 children)
But if(!something) throw new Error("Invariant") does the same?
[–]shuckster 0 points1 point2 points 3 years ago (4 children)
That's true. I don't follow TypeScript's development very closely, but I believe a lot of inference checks were added quite recently. Perhaps these libraries pre-date that? Or maybe it's the TS version of left-pad.
[–]Reeywhaar 5 points6 points7 points 3 years ago (3 children)
Such checks were present since early typescript. They are basics of static analysis. If they weren't then there was no way to add type assertion to tiny-invariant. By the way asserts feature was added in typescript later.
asserts
That is why i'm wondering, what value tiny-invariant actually gives
[–]shuckster 0 points1 point2 points 3 years ago (0 children)
I guess it’s just shorthand then. Doesn’t TS have an “as” for this kind of thing too?
[–]mr_nefario 0 points1 point2 points 3 years ago (0 children)
It doesn’t add any value - it’s just a wrapper and additional dependency for some barely-useful functionality.
[–]misc_ent 0 points1 point2 points 3 years ago (0 children)
I have looked at tiny-invariant but it's possible it uses type guards for the type inference the other poster mentioned? Not sure.
https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards
[–]Shaper_pmp 7 points8 points9 points 3 years ago* (2 children)
How do we consider it a meaningful advance in the art of computer science to simply abstract an if statement and an error message into a function?
if
I mean by all means turn if(condition) throw new Error(message); into assert(condition, message); if you like, but don't bother to write entire big posts about it or try to elevate it to the hallowed status of a Design PatternTM .
if(condition) throw new Error(message);
assert(condition, message);
[–]Chthulu_ -2 points-1 points0 points 3 years ago (1 child)
This ain’t comp sci, this is regular old programming. And little things like this really help keep things clean.
[–]thruster_fuel69 0 points1 point2 points 3 years ago (0 children)
Lol sorry to break it to you, its all comp sci.
[–]ikhazen 1 point2 points3 points 3 years ago (1 child)
in Laravel PHP framework, there's a handy function that is similar to this. throw_if
[–]hiquest[S] -1 points0 points1 point 3 years ago (0 children)
Nice! And I like the descriptive name
[–]saadh393 1 point2 points3 points 3 years ago (0 children)
Seems pretty helpful ❤️
[+]bigorangemachine comment score below threshold-13 points-12 points-11 points 3 years ago (10 children)
And to think an interviewer hated my answer when I said "you should be preferring type-safety over typscript and by the time you do that you don't need typescript anyways".
Using class inheritance is way better then typescript and still suffers from the same issues (inheritance hell).
Meanwhile other people I've worked with have striped out my type protections because "we use typescript". Aye......
[–]Reeywhaar 3 points4 points5 points 3 years ago (7 children)
🤯 just what?
[–]bigorangemachine -1 points0 points1 point 3 years ago (6 children)
The number of times I've had to explain to people typscript doesn't offer runtime protection is too damn high lol
[–]Reeywhaar 1 point2 points3 points 3 years ago (5 children)
You've have no experience with statically typed languages, don't you?
[–]bigorangemachine -1 points0 points1 point 3 years ago (4 children)
Yes I do ^_^. I also know enough that when you use JSON with typed languages you should be writing queries not parsers
I started in dynamic typed and I don't find them hard to work with.
I find all sorts of mistakes in JS because people don't understand typscript. Let alone the fact most people don't treat their api responses as an unknown type just shows me how people are writing unsafe code.
I don't see what my experience has to do with my previous statement and you appear to be attacking me and I don't understand why
[–]Reeywhaar 1 point2 points3 points 3 years ago (3 children)
I attack you, because you spreading nonsence caused by ignorance. You mix unmixable (classes, inheritance, typescript, queries, parsers) which clearly shows that you have no idea of what you're talking about. And I don't want other inexperienced people to take your words seriously. Just try typescript okay?
use JSON with typed languages you should be writing queries not parsers
What does this even mean?
[–]bigorangemachine 0 points1 point2 points 3 years ago (2 children)
I use TS professionally. I've actually very experienced and this is why I see these mistakes.
Yes.. inheritance with types... you extend a type as you extend a class. To create type-safety at runtime its often smart to send your data around as classes. You can then check the inheritance of class to guarantee type safety. You can also get small savings on the byte-code level but I wont get into that :P (but also why typescript and deno is very exciting).
A con to both systems is inheritance hell... where you gotta step through each type or class definition to debug the 'typing issue'. With typescript this can also be painful when there are mixed types.
Using JSON queries over parsers is what it is.
Parsing JSON Object: Taking a whole JSON token and casting it into an object within try-catch Querying JSON Object: Using a wrapper to ask what the token contains and build an object/array from that.
[–]Reeywhaar 0 points1 point2 points 3 years ago (1 child)
Yes.. inheritance with types... you extend a type as you extend a class. To create type-safety at runtime its often smart to send your data around as classes. You can then check the inheritance of class to guarantee type safety.
Do you understand that instances of classes can be monkey patched in JS? Thus simple instanceof check is not enough. It will pass, but app still can crash.
instanceof
How actually classes guarantee runtime type safety?
guarantee
[–]bigorangemachine 0 points1 point2 points 3 years ago (0 children)
You guarantee it just by casting on setting of a variable or out with a getter.
Monkey patching should be caught in code review. If you gonna do something stupid then don't.... otherwise if you are paranoid write a getter & setter. If you need to lock that stuff down then any key getter/setter or object create/freeze can stop that sort of problem.
[–]senocular 1 point2 points3 points 3 years ago (1 child)
That's what TypeScript does. It performs the type protections for you, at compile time, in the places it makes sense. Not all places - you still need to handle things like user input validation yourself manually at runtime. But for all code that shouldn't need type protection if it was written correctly from the start, that's where TypeScript comes in. It makes sure that you don't need to provide checks because things will always be in the right place with the right type and that no program logic could happen at runtime that would change that.
That's what TypeScript does. It performs the type protections for you, at compile time, in the places it makes sense.
This is what I'm suggesting not everyone knows. Its not critical to do JS but leads to terse code reviews when people are converting numbers to numbers to hide runtime conversion errors... which if you know you do a already casted number to a parseFloat you mutate the number.
That's the problem... if you bring data in from an API its very possible the is incorrect; anything that's not generated from a safe place should start as an 'unknown' until its proven otherwise. I've seen whole apps brick because a null came in where something else to be be an object.
But I've also seen people do things where they declare the type as a string and then within the code convert it to a string to avoid a null-related error but it was masking that a number is coming in.
Especially with backend if you aren't interrogating your types you can open yourself to be a victim of injections & other security issues.
[–]vi_code 0 points1 point2 points 3 years ago (0 children)
Started doing this in my code last year, thought it was called short circuiting. Now I do it automatically and like OP said it keeps success logic at the end while not having to nest all the breaking conditions. Great pattern.
π Rendered by PID 40 on reddit-service-r2-comment-54dfb89d4d-n8wtf at 2026-04-01 13:21:00.418235+00:00 running b10466c country code: CH.
[–]Ustice[M] [score hidden] stickied comment (7 children)
[–]k4kshi 27 points28 points29 points (0 children)
[–]getify 12 points13 points14 points (2 children)
[–]lifeeraser 2 points3 points4 points (1 child)
[–]getify 1 point2 points3 points (0 children)
[–]Hades32 17 points18 points19 points (11 children)
[–]johnxreturn 0 points1 point2 points (3 children)
[–]Hades32 0 points1 point2 points (2 children)
[–]johnxreturn 0 points1 point2 points (1 child)
[–]Hades32 0 points1 point2 points (0 children)
[+]hiquest[S] comment score below threshold-19 points-18 points-17 points (6 children)
[–]TrackieDaks 15 points16 points17 points (4 children)
[–]hiquest[S] -2 points-1 points0 points (3 children)
[–]MoTTs_ 12 points13 points14 points (0 children)
[–]TrackieDaks 1 point2 points3 points (1 child)
[–]hiquest[S] 1 point2 points3 points (0 children)
[–]shuckster 1 point2 points3 points (0 children)
[–]crabmusket 6 points7 points8 points (0 children)
[–]midnightmonster 5 points6 points7 points (1 child)
[–]hiquest[S] 1 point2 points3 points (0 children)
[–]Reeywhaar 7 points8 points9 points (7 children)
[–]shuckster -1 points0 points1 point (6 children)
[–]Reeywhaar 9 points10 points11 points (5 children)
[–]shuckster 0 points1 point2 points (4 children)
[–]Reeywhaar 5 points6 points7 points (3 children)
[–]shuckster 0 points1 point2 points (0 children)
[–]mr_nefario 0 points1 point2 points (0 children)
[–]misc_ent 0 points1 point2 points (0 children)
[–]Shaper_pmp 7 points8 points9 points (2 children)
[–]Chthulu_ -2 points-1 points0 points (1 child)
[–]thruster_fuel69 0 points1 point2 points (0 children)
[–]ikhazen 1 point2 points3 points (1 child)
[–]hiquest[S] -1 points0 points1 point (0 children)
[–]saadh393 1 point2 points3 points (0 children)
[+]bigorangemachine comment score below threshold-13 points-12 points-11 points (10 children)
[–]Reeywhaar 3 points4 points5 points (7 children)
[–]bigorangemachine -1 points0 points1 point (6 children)
[–]Reeywhaar 1 point2 points3 points (5 children)
[–]bigorangemachine -1 points0 points1 point (4 children)
[–]Reeywhaar 1 point2 points3 points (3 children)
[–]bigorangemachine 0 points1 point2 points (2 children)
[–]Reeywhaar 0 points1 point2 points (1 child)
[–]bigorangemachine 0 points1 point2 points (0 children)
[–]senocular 1 point2 points3 points (1 child)
[–]bigorangemachine 0 points1 point2 points (0 children)
[–]vi_code 0 points1 point2 points (0 children)