In the following code, although everything looks good but TypeScript shows an error which seems strange:
class Sample {
private value = 1;
private incrementValue(): void {
this.value++;
}
private beginTest(): void {
if (this.value !== 1) {
throw "bad state!"
}
this.incrementValue();
if (this.value != 2) {
throw "bad state!"; // ERROR!!: This condition will always return 'true' since the types '1' and '2' have no overlap.
}
}
}
Why should TypeScript raise such error? I'm changing value
by this.incrementValue()
. It seems this kind of error should be an more of an IntelliSense Warning rather than a Compile Error.
Possibly it is because of the gradual type system, but as it fails it these situations there should be a compile option to workaround this.
Is there any workaround or special compile option to turn this off?
While TypeScript's type inference is usually very good at inferring types correctly, the language has limited control flow analysis. The lead developer on the TypeScript team at Microsoft argues:
The primary question is: When a function is invoked, what should we assume its side effects are?
One option is to be pessimistic and reset all narrowings, assuming that any function might mutate any object it could possibly get its hands on. Another option is to be optimistic and assume the function doesn't modify any state. Both of these seem to be bad.
— Ryan Cavanaugh, Trade-offs in Control Flow Analysis
In the first if-statement TypeScript evaluates this.value
as a number type.
Right after the first if-statement, TypeScript determines that this.value
is no longer of the type number
. Instead it is of the literal type 1
.
This is because that any value that is different from 1 (!== 1
) would have terminated the function. Therefore, any value that doesn't terminate and instead moves on to the next if-statement, must be the exact literal value 1
.
When you call this.incrementValue();
, TypeScript isn't aware that the function mutates the state of this.value
. By the time you get to the 2nd if statement, TypeScript still believes that this.value
is of the literal type 1
.
Because of this, it evaluates the 2nd if-statement the same way it would if you had replaced this.value
with the number literal 1
if (1 != 2)
(This also throws the exact same error)
As for your problem right now, the simplest solution would be to either:
this.value
is a number if ((this.value as number) != 2)
if (+this.value != 2)
// @ts-ignore
comment on the line above so the TypeScript compiler ignores the errorRyan Cavanaugh mentions a few ideas for improving TypeScript's control flow analysis:
pure
modifier on functions that says this function doesn't modify anything. This is a bit impractical as we'd realistically want this on the vast majority of all functions, and it doesn't really solve the problem since lots of functions only modify one thing so you'd really want to say "pure except for m"
volatile
property modifier that says this "this property will change without notice". We're not C++ and it's perhaps unclear where you'd apply this and where you wouldn't.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With