One way for this to work would be if the compiler inlined some function calls when performing control flow analysis, meaning that this:
function toUpperCase(x: unknown) {
if (isString(x)) {
x.toUpperCase();
}
}
would need to be analyzed as if the body of isString()
were expanded out like this:
function toUpperCase(x: unknown) {
if (typeof x === "string") {
x.toUpperCase();
}
}
The canonical issue in GitHub talking about this is microsoft/TypeScript#9998. The question asked in that issue is "When a function is invoked, what should we assume its side effects are?" And there don't seem to be perfect answers. It's said that the full inlining solution for all function calls "wouldn't be even remotely practical". Could shallow inlining be done that behaves as you want for isString()
without completely bogging down the compiler? Probably, but is it worth it? Not sure. It doesn't look like there's much progress being made there; if you feel strongly enough about it and have some compelling ideas you can contribute to the GitHub issue.
Another way for this to work would be if the compiler automatically inferred type predicate return types for the right sorts of boolean
-returning functions, meaning that this:
function isString(s: unknown) {
return typeof s === "string";
}
would need to be inferred as if it were this:
function isString(s: unknown): s is string {
return typeof s === "string";
}
The canonical issue in GitHub talking about this is microsoft/TypeScript#16069. A previous version of this was declined as being too complex because again, it is not practical for the compiler to be able to analyze every boolean-returning function and figure out if there are any type-predicate-like implications for its return type. #16069 is still open, though, presumably with the intent of picking the low-hanging fruit of "simple" functions like x => typeof x === "string"
. And again, it's not clear if there is any progress being made there, so if you feel strongly enough about it and have some compelling ideas you can contribute to the GitHub issue.
But in neither of those cases would I hold my breath if I were you. The idiomatic solution for TS3.6 would be to just annotate isString()
as a user-defined type guard returning s is string
and move on to other things.
Okay, hope that helps you. Good luck!