Is there an equivalent to Typescript's exlamantion point in Python?

Viewed 960

I'm starting to use Type hints in Python. I have the following function:

def func(arg: Optional[str]):
    # ...
    # make sure arg is a string and not None
    do_something(arg)  # do_something expects a string

The call to do_something can only be reached if arg is indeed a string and not None, but mypy doesn't figure it out on its own (it's not a mypy issue, it can't understand everything). So mypy flags the call to do_something because arg may be None

In Typescript, a similar situation can be handled with arg! - the exclamation point tells the static analyzer that arg is not undefined. Is there something similar in Python?

3 Answers

Mypy shouldn't flag anything if your logic contains:

if arg is None:
    return None

If that is not possible you can also put an assertion before the warning:

assert x is not None

There are a couple of fundamental differences between TypeScript and Python with mypy. It is closer to how ECMAScript with Google Closure Compiler works than how TypeScript works. Or, how modern IDEs use type hints in documentation comments.

First off, TypeScript has a type system and typing rules. Python does not have a type system or typing rules. Python only has syntax for type hints. However, Python itself does not do anything with those type hints. This is left to third-party tools. This has some advantages and some disadvantages. The biggest advantage is that you can choose the tool that is most useful for your particular application domain. The biggest disadvantage is that when you write down a type hint, you don't actually know what it means … because it has no meaning. The meaning is assigned by an external third-party tool.

In other words: Python type hints have no defined semantics.

The second difference is that TypeScript has a separate type language. Python's type language instead is just Python. That means that a type hint must be a syntactically valid Python expression. In fact, type hints are evaluated at runtime, so not only do they have to be syntactically valid Python, they need to be semantically valid Python as well; they need to be executable without errors or exceptions, even if you never actually use them at runtime and only use them for static type checking e.g. with mypy. (That is the main reason for the typing module: to provide functions and objects that allow you to turn syntax that you would like to write (e.g. generics) into valid runtime expressions.)

In other words: Python type hints have no separate syntax, they are just Python expressions.

So, the first thing this tells us is that whatever potential solution you come up with, it must be syntactically and semantically valid Python. arg! is not syntactically valid Python. So, an exact equivalent to the TypeScript syntax is not possible.

So, we have to check whether we can find a semantic equivalent with a different syntax. The possible places where we could find one are:

Looking at those three, we find that we don't find anything. This isn't proof that something like this doesn't exist (absence of evidence is not evidence of absence), but it is a good indication.

Mypy only supports the equivalent of a TypeScript Type Guard:

if arg is None:
    pass
else:
    # mypy knows that `arg` is a `str` here

if arg is not None:
    # mypy knows that `arg` is a `str` here

if not arg:
    pass
else:
    # mypy knows that `arg` is a `str` here

if arg:
    # mypy knows that `arg` is a `str` here

In case you cannot provide such a type guard, or you absolutely 100% know arg cannot be None at this point, under no circumstance, EVER, you can provide a type assertion:

assert arg is not None
# mypy knows that `arg` is a `str` here

A type assertion will also let mypy know that arg cannot be None after that point. This is explicitly documented in the mypy documentation.

This might be the closest thing semantically to the TypeScript ! type operator. In fact, the ! operator is really nothing but an assertion: "Hey, type checker, trust me, I know something you don't." Which is exactly the same thing the Python assertion also says.

There is no null-safety in python.

The best you can do is:

if arg is not None:
    do_something(arg)

Or if you want to raise an exception:

if arg is None:
    raise Exception("arg == none")

do_something(arg) # arg will not be None

Also, type hints mean nothing in normal python because this is only visual, nothing is checked. For exemple, the following code work fine:

def x(y: int):
    return y

x("hello")
Related