Improve typing, with ! to force not None value

Consider the following example

from typing import Optional


def foo(name: str) -> Optional[str]:
    if name == "foo":
        return "foo"
    return None


bar: str = foo("foo")

With mypy the errors out with

main.py:10: error: Incompatible types in assignment (expression has type "Optional[str]", variable has type "str")
Found 1 error in 1 file (checked 1 source file)

It would be great if ! could be used at the end to force the type checker that the value is not None
like this

from typing import Optional


def foo(name: str) -> Optional[str]:
    if name == "foo":
        return "foo"
    return None


bar: str = foo("foo")!

1 Like

Does this do anything more than cast(str, foo("foo"))? Not really a fan since cast does all the things it does and express the intent clearer.

Ok consider this exmaple:

from typing import Optional


class FooBar:
    name: Optional[str]

    def __init__(self, name: Optional[str] = None) -> None:
        self.name = name


def foo(name: str) -> Optional[FooBar]:
    if name == "foo":
        return FooBar()
    return None


bar: str = foo("foo").name

It errors out with

main.py:13: error: Missing positional argument "name" in call to "FooBar"
main.py:17: error: Item "None" of "Optional[FooBar]" has no attribute "name"
main.py:17: error: Incompatible types in assignment (expression has type "Union[str, None, Any]", variable has type "str")
Found 3 errors in 1 file (checked 1 source file)

It would require multiple cast to do so where as all can be done with adding a ! at the end @uranusjr

The exact syntax you propose is available in Hypertag (http://hypertag.io/), a Python-inspired language for templating and document generation. There are two different postfix operators (“qualifiers”) that mark a subexpression as either “optional” (?) or “obligatory” (!). The latter enforces a true (non-empty, not-None) value of the expression, otherwise an exception is raised.

Obviously, this operator is applied to ordinary expressions (type declarations don’t exist in Hypertag), still it might do as a related implementation for what you propose. Details can be found here:

http://hypertag.io/#non-standard-postfix-operators

1 Like

I would also see benefit in this:

It’s more or less shorter syntax:

cast(SomeSlightlyLengthierClassName, get_it_somewhere()).some_prop  # awful
get_it_somewhere()!.some_prop  # nice!

While cast() is a super-set, it’s much more verbose.

Also, what if I rename SomeSlightlyLengthierClassName? Fair enough, my IDE’s refactoring features should also rename the reference.

But what if I change get_it_somewhere() to return some other type? I need to manually iterate the code (let’s say I add a child class like SomeSlightlyLengthierChildClassName and return that instead).

If we don’t want to add new syntax to the language, how about a new function for this. It could be called something like unwrap() and would basically be sugar for the long form cast().

unwrap(get_it_somewhere()).prop

Or maybe I’ve been looking at Rust code for too long.