Logo
Published on

Typescript-like Types in Python 3

Authors
  • avatar
    Name
    Rohan Hussain
    Twitter

As a reference, you can use this site.

The need for types is strongly felt when calling functions. You need to know the exact types of the arguments of a function when calling it, otherwise the function may not work. For example, the sum function:

def sum(x, y):
    return x + y

To give an integer type to its arguments, you can use a typescript-like syntax:

def sum(x: int, y: int):
    return x + y

You can also give the function's return value a type using an arrow ->:

def sum(x: int, y: int) -> int:
    return x + y

Note that Python will not throw a runtime error if one of these type checks fails.

VS Code Configuration

VS Code, by default, may not show you any error flags if you violate one of these type checks. To enable those error flags, go to VS Code settings (JSON) and set the following flag:

  "python.analysis.typeCheckingMode": "basic",

Its value is usually "off" by default.

Other Types

You can use any of the built-in types of Python, such as:

  • int
  • float
  • str
  • list
  • tuple
  • dict: For a dictionary:
        def print_dictionary(my_dict: dict):
            for key, value in my_dict.items():
                print (key, value)
    

Dictionaries

You can also use typing.Dict which is more generic and lets you specify the types of the dictionary's keys and values as well. StackOverflow. However, as of Python 3.9, you should use dict as typing.Dict is deprecated.

Say our dictionary has string keys and integer values:

    from typing import Dict

    def print_dictionary(my_dict: Dict[str, int]):
        for key, value in my_dict.items():
            print (key, value)

As of Python 3.9, the following will also work:

    def print_dictionary(my_dict: dict[str, int]):
        for key, value in my_dict.items():
            print (key, value)

Typed Dictionaries

What if you want to specify the exact keys that a dictionary will have? For this, we have typing.TypedDict. Reference.
You can use TypedDict with a class-based syntax or a functional syntax.

Class-based Syntax

To define a new dictionary type Movie that always contains keys name and year:

from typing import TypedDict

class Movie(TypedDict):
    name: str
    year: int

def announce_movie(movie: Movie):
    print("Movie",movie['name'],"was made in year", movie["year"])

announce_movie({'name': "Interstellar", 'year': 2014})

If you have configured VS Code properly, trying to access any other key on the movie dictionary will show you a red flag. Also note that in the above type declaration, both keys name and year are required.

Function Syntax

from typing import TypedDict

Movie = TypedDict('Movie', {'name': str, 'year': int})

def announce_movie(movie: Movie):
    print("Movie",movie['name'],"was released in year", movie["year"])

announce_movie({'name': "Interstellar", 'year': 2014})

Optional Keys

The following examples work in Python 3.9.
You can pass a third total=False argument to the functional TypedDict() call which makes all keys of the dictionary optional by default.

Note that if you used the old announce_movie() function despite the optional keys, you will see a type error:

from typing import TypedDict

Movie = TypedDict('Movie', 
                    {'name': str, 'year': int},
                        total=False)

def announce_movie(movie: Movie):
    print("Movie",movie['name'], 
                    "was released in year", movie["year"])

announce_movie({'name': "Interstellar"})

Your IDE would show you a type error on lines 8-9, saying that it is not guaranteed that the movie dictionary will contain keys name and year.
To avoid that error:

from typing import TypedDict

Movie = TypedDict('Movie', 
                    {'name': str, 'year': int},
                        total=False)

def announce_movie(movie: Movie):
    if 'name' in movie:
        print("Movie name is", movie['name'])
    if 'year' in movie:
        print("It was released in", movie["year"])

announce_movie({'name': "Interstellar"})

You could also set some keys in the dictionary as required, for example, the name:

from typing import TypedDict
from typing_extensions import Required

Movie = TypedDict('Movie', 
                    {'name': Required[str], 'year': int},
                        total=False)

def announce_movie(movie: Movie):
    print("Movie", movie['name'], end='')
    if 'year' in movie:
        print("was released in", movie["year"])

announce_movie({'name': "Interstellar"})

This was one approach: declare all keys as optional by default and then set some as required. If the majority of the keys in your dictionary are required, then you can do the inverse. Let all keys be required by default (by removing the total=False argument to TypedDict), and make some optional by using typing_extensions.NotRequired.