TypeScript Version: 3.7.2
Search Terms: Type check does not work if it is done in a method call
Code
class SomeClass {
someNumber: number | undefined
private get someComputedNumber() {
this.validateSomeNumber()
return 1 + this.someNumber // ERROR: Object is possibly 'undefined'.
}
private validateSomeNumber() {
if (typeof this.someNumber === 'undefined') {
throw new Error('someNumber is undefined')
}
}
}
Expected behavior:
It should not error to "Object is possibly 'undefined'.", because if this.someNumber is undefined, this.validateSomeNumber will throw
Actual behavior:
It errors to "Object is possibly 'undefined'."
Playground Link: http://www.typescriptlang.org/play/?ssl=15&ssc=2&pln=1&pc=1#code/MYGwhgzhAEDKD2BbApgYXFaBvAUNf0ESyAcgK6IBGyATgFzQB2F1N0APtGYwCbIBmAS0bIeOPAQAONQQDcwAF2TQA5sgWFiqJJLJKe5KrQAUASmwSCBBQAtBEAHTyQgnouQIUh1mfFWrNOpkNIzQAIzQANTQtvYORF4stJbQAL5+UjLyStDOru6epEk0Zhb+BIL80MYKAJ6SyPBVsY4JRUZsALzd0ADk3HxCIjy95ljQKeX4tjTwAO5MyAsAojSzJb1t3rTQ9ly8AsKio5ME6VbpqUA
Method bodies are not inlined for CFA purposes
TS 3.7 provides assertion functions for complex cases, unfortunately they only assert their values so you'll have to change implementation to validate it's arguments.
Argument assertion example or more weird approach that asserts on this 😨
This is working as intended (or possibly a design limitation) and probably a duplicate of #9998. Control flow type analysis in the face of function or method calls is a hard problem to solve. Right now the compiler more or less assumes that function calls have no effect on control flow.
That being said, TypeScript 3.7 introduced assertion functions (#32695) which allow you to mark a function as having such an effect on control flow. So the specific code you have above could be changed to
private validateSomeNumber(): asserts this is { someNumber: number } {
if (typeof this.someNumber === 'undefined') {
throw new Error('someNumber is undefined')
}
}
and it will just work for you. Good luck!
@jcalz Thanks, this seems to work well :).
When I first read your answer, i though that simply adding a property to the class would break your solution, but it did not. I guess that asserts this is { someNumber: number } is not strict, because adding, for example, a someString: string | undefined property to the class is still working.
Most helpful comment
Method bodies are not inlined for CFA purposes