tiny-invariant
tiny-invariant copied to clipboard
What could a new tiny-invariant API look like? (Custom errors, prefixes, aliases etc)
A big goal of this library is to be as lite as possible. Yet, there are some common and useful features that would be great for this library to have:
- a
message
can be astring
or lazy (eg with afunction
for when your error message is expensive to compute) - The
Error
that is thrown should be able to be any Error (egMyCustomError
) or perhaps even any value (you might want to throw a promise for example) #166 -
"Invariant failed: "
prefix should be optional (or perhaps removed completely - do we even need a prefix?) #143 - I think this library should could to named imports only to reduce consumption (would require a codemode). Alternatively could just add two named exports (
invariant
andassert
) - We could add an
assert
named export (#153)
All of these features are possible. I am trying to think through:
- What would a nice™️ look like (I am currently thinking the second argument might need to be a string or an options object)
- How to achieve the above outcomes without weighing down people for features that they do not want to use? (a main goal of
tiny-invariant
is to be tiny)
Here is my favourite option I have come up with so far:
// basic usage
invariant(condition);
// With a basic message
//(maybe it's time we drop the built-in "Invariant failed: " prefix for minimum bundles too)
invariant(condition, message)
// advanced (breaking change)
invariant(condition, () => new Error('My expensive custom message'));
invariant(condition, () => new MyCustomError('Error message'));
invariant(condition, () => Promise.resolve(4));
API changes:
- `invariant(condition, string | () => string)`
+ `invariant(condition, string | () => unknown)`
The second argument is currently used to compute expensive error messages. I think the second argument could be used as a getter for the whole value that is going to be thrown. This gives consumers heaps of control.
Stripping error messages
Ideally, we remove invariant
error message content from production bundles to save on kbs
// author
invariant(condition, message);
// production build
invariant(condition);
This becomes more tricky when the second argument to invariant
has multiple purposes (error message creation AND error type).
Without additional tooling support, consumers would need to do something like this:
invariant(condition, () => MyCustomError(
process.env.NODE_ENV !== 'production' ? 'My error message': ''
)
Which is not great.
It would be fantastic to get other peoples thoughts on this idea, as well as other ideas as well! Please do not feel limited by my suggestion
Something I think about: what is the value of invariant
when you could write this code?
if(!condition) {
throw new MyCustomError(
process.env.NODE_ENV !== 'production' ? 'My error message': ''
)
}
I find invariant
helpful to use as it is just so convenient for writing single line type narrowing statements
invariant(condition1);
invariant(condition2);
invariant(condition3);
I'm using invariant do the form validate. if some input not match the requirement, I would like to show the message.
try {
invariant(validateForm(), 'readable message to tell user what is wrong')
} catch (e) {
toast(e.message)
}
But in production, the message got striped.
I think I would rather have a "treeShakeMessageInProd" utility to use w/ tiny-invariant
than having it built-in, for that purpose, so I can opt-in... though it's not much better anyways..
I use assert-ts
to allow directly consuming the resulting type of the call, like const x = assert(host?.tags);
Any updates on this? I would love to omit the prefix and throw custom errors. I wouldn't worry too much about the production build, as omitting the message can be done as described in the readme.
Is it really a good idea to assume that everyone will use the string literal production
to tag a production environment? There should be a way to customize this too.