Description
TypeScript Version: 2.5.3
function invariant(condition: any, message: string): void {
if (!condition) {
throw new Error(message);
}
}
function double(a: number | string) {
invariant(typeof a === 'number', 'Sorry, strings not supported yet');
return a * 2;
}
Expected behavior:
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):
declare function invariant(condition: any, message: string): void if testValue;
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 true
to 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 thattrue
is the only possible value, but something would certainly need to be in the signature ofinvariant
to indicate that it is has this behavior.That said, I don't see a lot of advantages to using
invariant
over simply sayingThis will work exactly as you want
invariant
to work, without any separate utility function that you need to define. If you want to augmentmessage
somehow, you could easily define anever
-returning function that does so:The project I work on uses this
crash
function quite liberally.RyanCavanaugh commentedon Oct 10, 2017
See #10421, #8655
sgoll commentedon Oct 10, 2017
@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. :)@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.