You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
invariant should establish a type guard for the code following the invariant statement.
Actual behavior:
TypeScript errors with error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type.
This is because a still resolves to type string | number.
Suggestion:
invariant ensures that its first argument condition is truthy, and throws otherwise, i.e. when the code following the invariant statement is reached it is safe to make all the same assumptions as if that code were wrapped in if (condition) { … }, or equivalently, the invariant were replaced with its literal implementation.
Although the latter works as a regular type guard (if (typeof a !== 'number') { throw … }), it makes the code unnecessarily verbose. I think there should be a way of declaring that a given function guarantees that some guard is active, and never returns otherwise, i.e. something like this (pseudo code):
I would expect the syntactic form of any such thing to be more like this:
functioninvariant(condition: boolean,message: string): condition is true{if(!condition){thrownewError(message);}returntrue;}
This doesn't work as you want it to (TS doesn't seem to infer from condition is true to mean that typeof a !== 'number' and carry that inference forward), and even if it did you would need to use a condition so maybe some new syntax could be used to indicate that true is the only possible value, but something would certainly need to be in the signature of invariant to indicate that it is has this behavior.
That said, I don't see a lot of advantages to using invariant over simply saying
if(typeofa!=='number')throw'Sorry, strings not supported yet';
This will work exactly as you want invariant to work, without any separate utility function that you need to define. If you want to augment message somehow, you could easily define a never-returning function that does so:
functioncrash(message: string): never{throw`Something crashed: ${message}.`;}functiondouble(a: number|string){if(typeofa!=='number')returncrash('Sorry, strings not supported yet');returna*2;}
The project I work on uses this crash function quite liberally.
Ryan Cavanaugh (@RyanCavanaugh) Thanks for the reference. Though I tried looking for existing issues, I could not find any that had the invariant application that I was looking for, at least not under that name. :)
Kevin Ryan (@krryan) I hadn't thought about splitting the check and message formatting/post-processing etc. into a separate function. Thanks for the suggestion. I like your example of crash and will look into that.
Activity
krryan commentedon Oct 10, 2017
I would expect the syntactic form of any such thing to be more like this:
This doesn't work as you want it to (TS doesn't seem to infer from
condition is trueto mean thattypeof a !== 'number'and carry that inference forward), and even if it did you would need to use a condition so maybe some new syntax could be used to indicate thattrueis the only possible value, but something would certainly need to be in the signature ofinvariantto indicate that it is has this behavior.That said, I don't see a lot of advantages to using
invariantover simply sayingThis will work exactly as you want
invariantto work, without any separate utility function that you need to define. If you want to augmentmessagesomehow, you could easily define anever-returning function that does so:The project I work on uses this
crashfunction quite liberally.RyanCavanaugh commentedon Oct 10, 2017
See #10421, #8655
sgoll commentedon Oct 10, 2017
Ryan Cavanaugh (@RyanCavanaugh) Thanks for the reference. Though I tried looking for existing issues, I could not find any that had the
invariantapplication that I was looking for, at least not under that name. :)Kevin Ryan (@krryan) I hadn't thought about splitting the check and message formatting/post-processing etc. into a separate function. Thanks for the suggestion. I like your example of
crashand will look into that.