Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
alexreardon opened this issue Jan 16, 2023 · 6 comments

Comments

@alexreardon
Copy link
Owner

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) CusomError #166
  • "Invariant failed: " prefix should be optional (or perhaps removed completely - do we even need a prefix?) Option to return error without the 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 (More meaningful name for invariant function #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 alexreardon changed the title What should a new tiny-invariant API look like? What could a new tiny-invariant API look like? (Custom errors, prefixes, aliases etc) Jan 16, 2023
@alexreardon
Copy link
Owner Author

alexreardon commented Jan 16, 2023

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
Copy link
Owner Author

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

@lili21
Copy link

lili21 commented Apr 7, 2023

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.

@jasikpark
Copy link

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

@daviddavid
Copy link

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.

@nyngwang
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants