> A very common function in our source code is the invariant function. I can't explain it any better than the documentation does
Seems like you'd have an easier time explaining what it does if you'd just called it "assert()" in the first place. This isn't a new concept, why does it have a new name?
A function by that name is apparently treated specially for flow. My guess is it interprets it like the if statement OP ended up using, where applies the first condition to the type checking.
How so? The major design by contract language I know of is D, and its contracts are essentially asserts that are either placed at the beginning of the body or prior to all returns.
You can certainly get substantially similar behavior just using asserts at the beginning of the function body and ensuring asserts at the end before a return are hit by all code paths (e.g. make an anonymous function that contains the body, then call it wrapped in a try/catch and assert on the return value.
Unless I'm missing something about design by contract, it's essentially just asserts at the beginning and end of a function.
Precondition (called invariant here): a set of conditions that must be met in order to call a method. Another name for argument validation, but it can be other things (e.g. connected must be true before calling methods on a connection).
Postcondition: guarantees that a method makes. For instance, it won't return null.
Invariants (incorrectly used): conditions that always hold true on a type, e.g. the starting index on a slice must always be greater or equal to zero.
I'm curious about the matching for the Maybe type (type Maybe<T> = T | void;). Is void an alias or superset of undefined? Perhaps because undefined is a reserved word? Wouldn't it make more sense if it was either:
assert() is not run in release or optimized builds in any language I'm aware of, while invariant() is in the place I've seen it used. Similar to CHECK() in C++ codebases.
Invariants in design by contract are usually also not run in release/optimized builds.
With asserts, it's not uncommon to provide a separate switch, that defaults to excluding them in release/optimized builds, but you can override. And some languages make the distinction explicit in asserts themselves - e.g. in C# (and any other .NET language that uses the stdlib for this), Debug.Assert will be excluded #if DEBUG, but Trace.Assert will not.
Seems like you'd have an easier time explaining what it does if you'd just called it "assert()" in the first place. This isn't a new concept, why does it have a new name?