What is Typing?
Typing is a bug deterrent. It acts as a strong code linter that allows your code to check itself.
What does it mean to check itself? When we type a function in Python or Javascript, we explicitly define the data type and data structures that are allowed to be passed as an argument.
If the function detects that it may receive (or receives) an argument other than the declared type it can warn the developer or error out during compilation.
Why Type Check?
I have already been mentioned that typing helps us prevent the introduction of bugs into our software by being verbose about what is allowed.
This can catch mistakes before they occur. Furthermore, typing allows us to create documentation for our code as we go. It helps us describe the functions' inputs and outputs.
Some other (more advanced?) programming languages have typing built into them. This includes (but don't quote me on this):
- C and C++
- Java
- Typescript
Python and Javascript are criticized for lacking type checks. That's why things like Typescript and the typing
module were created.
typing
module
Support for type hints (Python Docs)
This module does not strictly enforce typing with Python. However, it can provide helpful hints or warnings through IDEs.
We can type our functions that use built-in types like str
, list
, and it has support for more exotic types that are used in asynchronous applications. You can dive deep into type checking and the time is typically well spent.
A few more things we can do with the module:
- create type aliases
- build new types
- check our type definitions (
get_type_hints()
)
Providing docstrings is often enough for other developers (or you) to properly use a function. However, type checks may be more your style, or they can add an extra layer of structure to your scripts.
Example of Type Checking in Python
In this section, we'll quickly lay out a few examples of using the typing
module with Python.
Starting with a very basic example, let's create a function with one typed argument and print the type hints to the console.
from typing import get_type_hints
def add_2(number: int) -> int:
return number + 2
print(add_2(4))
# 6
print(get_type_hints(add_2))
# {'number': <class 'int'>, 'return': <class 'int'>}
The following example works in Python ^3.9.1. We are saying the items
takes in a list of strings.
from typing import get_type_hints
def print_rows(items: list[str]) -> None:
for i in range(len(items)):
print(items[i])
print("\n")
print(print_rows(["potatoes", "carrots", "onions"]))
##potatoes
##
##
##carrots
##
##
##onions
##
##
##None
print(get_type_hints(print_rows, include_extras=True))
##{'items': list[str], 'return': <class 'NoneType'>}
What if our list accepts strings or integers? We have to import Union
from typing
.
from typing import get_type_hints, Union
def print_rows(items: list[Union[str, int]]) -> None:
for i in range(len(items)):
print(items[i])
print("\n")
print(print_rows(["potatoes", "carrots", "onions"]))
##potatoes
##
##
##carrots
##
##
##onions
##
##
##None
print(get_type_hints(print_rows, include_extras=True))
##{'items': list[typing.Union[str, int]], 'return': <class 'NoneType'>}
This is starting to get messy. Thankfully, Python has an answer for that. We can create a type alias and pass that into the function instead!
from typing import get_type_hints, Union
GroceryItems = list[Union[str, int]]
def print_rows(items: GroceryItems) -> None:
for i in range(len(items)):
print(items[i])
print("\n")
print(print_rows(["potatoes", "carrots", "onions"]))
##potatoes
##
##
##carrots
##
##
##onions
##
##
##None
print(get_type_hints(print_rows))
##{'items': list[typing.Union[str, int]], 'return': <class 'NoneType'>}
We get the exact same output.
Conclusion
Typing is cool, but it's overlooked. For reasons that are beyond developers' understanding, unless we are required to type check, we usually don't.
It's comparable to working out. We don't notice the benefits until later (sometimes too late!).
Thanks for reading!