No, that's the entire point of my question. In Typescript (and Kotlin, as others have noted), the type system knows within the scope of that if statement that obj is a string, so it lets you call string-specific methods on obj withOUT introducing the new variable s.
if (typeof obj === 'string') {
// obj is a string
}
is really not anything meaningfully different than this
if (obj is string as obj) { // as in C#
// obj is a string (shadowing the outer scoped obj)
}
or this
if (obj instanceof String obj) {
// obj is a String here
}
you can imagine a pseudo TypeScript language like this
if (typeof obj === 'string' as obj) {
// obj is a string
}
Actual TypeScript merely allows you to elide the "as obj" and all it needs to trigger this is the "typeof obj === <string literal>" inside of an if expression. This can be done by a simple syntactic replacement, it doesn't require control flow analysis to get this specific feature.
But yes, if you have a more general computed expression, that would apply, but that was not the case you were stating.
Ie the variable isn't the issue (redefining in this case seems like a distinction without a difference)... However this does work in TS, which is a demonstration of CFA:
const isStr = typeof obj === "string";
if (isStr) {
// obj is str
}
For what it's worth I think CFA is useful in TypeScript based on it at its core being a structural typed bandaid over JS, but I think these specific CFA type narrowing features are redundant in stronger typed languages.
match thing {
case a: String =>
// use a as string
case a @ MyCaseClass(b: String, c, d) =>
// use a as the instance of MyCaseClass or use b as a string
}
https://openjdk.java.net/jeps/305