takeIf and smart cast

When calling takeIf on a value, providing a predicate that checks if the receiver is of a given type does not create a smart cast to that type. For instance, the following code snippets are functionally equivalent, yet in the former, someVal is smart casted, while in the latter, it is not. Is there something I’m missing for why it doesn’t get smart casted in the second snippet?

//Smart cast works for this
val someVal: Any = "Hello"
if (someVal is String) { print(someVal.toLowerCase()) }

//Smart cast doesn't work for this
val someVal: Any = "Hello"
someVal.takeIf { it is String }?.let { print(it.toLowerCase()) }

In the second snippet the type check occurs in the lambda body. The compiler can neither prove that checks that happen inside lambda somehow affect the returned value of takeIf, nor even that lambda is executed at all.

Meanwhile you can use as? safe cast operator instead of takeIf with is check:

(someVal as? String)?.let { print(it.toLowerCase()) }
2 Likes

It never even occurred to me that as had a safe version, but it will certainly prove useful now that I know it exists. Thanks!