Typescript couldn't actually do this for interfaces. Typescript interfaces don't exist post-compilation, and the only way to check for them would be to ensure that every expected property exists on the incoming object.
This has a very annoying side effect of never being able to exclude interfaces from a union type:
function doStuff(thing : string | SomeInterface) : void {
if (typeof(thing) === "string") {
// thing is still typed as string | SomeInterface,
// because there's no guarantee that the
// string object doesn't implement the interface.
// If SomeInterface was a class instead of an interface,
// then thing would only have the string type here.
}
}
Also, this is ignoring the fact that you would also need to check the parameter types on an incoming function, which AFAIK isn't possible at all.
I work with TypeScript fairly closely and was surprised at your example, because I was sure that this already works. It turns out the type narrowing is actually special-cased on only some particular AST. You can see it working here, where "charAt" would not be allowed if it was still |SomeInterface:
This has a very annoying side effect of never being able to exclude interfaces from a union type:
Also, this is ignoring the fact that you would also need to check the parameter types on an incoming function, which AFAIK isn't possible at all.