tiny-invariant icon indicating copy to clipboard operation
tiny-invariant copied to clipboard

What could a new tiny-invariant API look like? (Custom errors, prefixes, aliases etc)

Open alexreardon opened this issue 2 years ago • 6 comments

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 a string or lazy (eg with a function for when your error message is expensive to compute)
  • The Error that is thrown should be able to be any Error (eg MyCustomError) 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 and assert)
  • 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)

alexreardon avatar Jan 16 '23 05:01 alexreardon

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

alexreardon avatar Jan 16 '23 05:01 alexreardon

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);

alexreardon avatar Jan 16 '23 07:01 alexreardon

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.

lili21 avatar Apr 07 '23 10:04 lili21

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);

jasikpark avatar Jun 13 '23 20:06 jasikpark

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.

daviddavid avatar Nov 12 '23 10:11 daviddavid

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.

nyngwang avatar Mar 28 '24 02:03 nyngwang