lists.arthurdejong.org
RSS feed

python-stdnum branch master updated. 1.20-33-g0e857fb

[Date Prev][Date Next] [Thread Prev][Thread Next]

python-stdnum branch master updated. 1.20-33-g0e857fb



This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "python-stdnum".

The branch, master has been updated
       via  0e857fb8c5ba500a80cd20553bff488bf46b7643 (commit)
      from  bc689fdddfb9dfa5ba60ad7bd08fb2533086d3cb (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
https://arthurdejong.org/git/python-stdnum/commit/?id=0e857fb8c5ba500a80cd20553bff488bf46b7643

commit 0e857fb8c5ba500a80cd20553bff488bf46b7643
Author: David Salvisberg <david.salvisberg@seantis.ch>
Date:   Thu Mar 13 14:15:54 2025 +0100

    Add type hints
    
    Closes https://github.com/arthurdejong/python-stdnum/pull/467
    Closes https://github.com/arthurdejong/python-stdnum/issues/433

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index fd2f644..af22673 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -30,7 +30,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        tox_job: [docs, flake8, headers]
+        tox_job: [docs, flake8, mypy, headers]
     steps:
       - uses: actions/checkout@v3
       - name: Set up Python
diff --git a/.gitignore b/.gitignore
index bc90af0..77e8d2c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,7 +6,10 @@ __pycache__
 
 # /
 /.coverage
+/.mypy_cache
+/.ruff_cache
 /.tox
+/.venv
 /build
 /coverage
 /dist
diff --git a/docs/index.rst b/docs/index.rst
index e9c76c3..04d9ecd 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -9,7 +9,7 @@ Common Interface
 
 Most of the number format modules implement the following functions:
 
-.. function:: module.validate(number)
+.. function:: module.validate(number: str) -> str
 
    Validate the number and return a compact, consistent representation of
    the number or code. If the validation fails,
@@ -19,7 +19,7 @@ Most of the number format modules implement the following 
functions:
    :raises ValidationError: When the specified number is invalid
    :returns:  str -- A compact (canonical) representation of the number
 
-.. function:: module.is_valid(number)
+.. function:: module.is_valid(number: str) -> bool
 
    Return either ``True`` or ``False`` depending on whether the passed number
    is in any supported and valid form and passes all embedded checks of the
@@ -27,7 +27,7 @@ Most of the number format modules implement the following 
functions:
 
    :returns: bool -- ``True`` if validated, ``False`` otherwise
 
-.. function:: module.compact(number)
+.. function:: module.compact(number: str) -> str
 
    Return a compact representation of the number or code. This function
    generally does not do validation but may raise exceptions for wildly
@@ -35,7 +35,7 @@ Most of the number format modules implement the following 
functions:
 
    :returns: str -- The compacted number
 
-.. function:: module.format(number)
+.. function:: module.format(number: str) -> str
 
    Return a formatted version of the number in the preferred format.
    This function generally expects to be passed a valid number or code and
@@ -45,7 +45,7 @@ Most of the number format modules implement the following 
functions:
 
 The check digit modules generally also provide the following functions:
 
-.. function:: module.checksum(number)
+.. function:: module.checksum(number: str) -> int
 
    Calculate the checksum over the provided number. This is generally a
    number that can be used to determine whether the provided number is
@@ -53,7 +53,7 @@ The check digit modules generally also provide the following 
functions:
 
    :returns: int -- A numeric checksum over the number
 
-.. function:: module.calc_check_digit(number)
+.. function:: module.calc_check_digit(number: str) -> str
 
    Calculate the check digit that should be added to the number to make it
    valid.
diff --git a/setup.cfg b/setup.cfg
index 88f795a..2bff8c7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -36,7 +36,10 @@ max-complexity = 15
 max-line-length = 120
 extend-exclude =
   .github
+  .mypy_cache
   .pytest_cache
+  .ruff_cache
+  .venv
   build
 
 [isort]
@@ -47,3 +50,8 @@ known_third_party =
   openpyxl
   requests
   xlrd
+
+[mypy]
+python_version = 3.9
+strict = True
+warn_unreachable = True
diff --git a/setup.py b/setup.py
index d652ba9..e50ef71 100755
--- a/setup.py
+++ b/setup.py
@@ -76,7 +76,7 @@ setup(
     ],
     packages=find_packages(),
     install_requires=[],
-    package_data={'': ['*.dat', '*.crt']},
+    package_data={'': ['*.dat', '*.crt', 'py.typed']},
     extras_require={
         # The SOAP feature is only required for a number of online tests
         # of numbers such as the EU VAT VIES lookup, the Dominican Republic
diff --git a/stdnum/__init__.py b/stdnum/__init__.py
index 5eb2c05..85da9ef 100644
--- a/stdnum/__init__.py
+++ b/stdnum/__init__.py
@@ -37,6 +37,8 @@ Apart from the validate() function, many modules provide extra
 parsing, validation, formatting or conversion functions.
 """
 
+from __future__ import annotations
+
 from stdnum.util import get_cc_module
 
 
diff --git a/stdnum/ad/__init__.py b/stdnum/ad/__init__.py
index 09ccf97..1c7c03b 100644
--- a/stdnum/ad/__init__.py
+++ b/stdnum/ad/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Andorran numbers."""
 
+from __future__ import annotations
+
 # Provide aliases.
 from stdnum.ad import nrt as vat  # noqa: F401
diff --git a/stdnum/ad/nrt.py b/stdnum/ad/nrt.py
index 90c1044..43a6a56 100644
--- a/stdnum/ad/nrt.py
+++ b/stdnum/ad/nrt.py
@@ -44,11 +44,13 @@ InvalidComponent: ...
 'D-059888-N'
 """  # noqa: E501
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -57,7 +59,7 @@ def compact(number):
     return clean(number, ' -.').upper().strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Andorra NRT number.
 
     This checks the length, formatting and other constraints. It does not check
@@ -79,7 +81,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Andorra NRT number."""
     try:
         return bool(validate(number))
@@ -87,7 +89,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join([number[0], number[1:-1], number[-1]])
diff --git a/stdnum/al/__init__.py b/stdnum/al/__init__.py
index 25d7cd1..127fc7e 100644
--- a/stdnum/al/__init__.py
+++ b/stdnum/al/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Albanian numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.al import nipt as vat  # noqa: F401
diff --git a/stdnum/al/nipt.py b/stdnum/al/nipt.py
index 59e0f7f..55c92bf 100644
--- a/stdnum/al/nipt.py
+++ b/stdnum/al/nipt.py
@@ -50,6 +50,8 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -60,7 +62,7 @@ from stdnum.util import clean
 _nipt_re = re.compile(r'^[A-M][0-9]{8}[A-Z]$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' ').upper().strip()
@@ -71,7 +73,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length and
     formatting."""
     number = compact(number)
@@ -82,7 +84,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/ar/__init__.py b/stdnum/ar/__init__.py
index 6e99d4e..b7ddb44 100644
--- a/stdnum/ar/__init__.py
+++ b/stdnum/ar/__init__.py
@@ -20,6 +20,8 @@
 
 """Collection of Argentinian numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.ar import cuit as vat  # noqa: F401
 from stdnum.ar import dni as personalid  # noqa: F401
diff --git a/stdnum/ar/cbu.py b/stdnum/ar/cbu.py
index 08f6f98..6923f96 100644
--- a/stdnum/ar/cbu.py
+++ b/stdnum/ar/cbu.py
@@ -39,17 +39,19 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     weights = (3, 1, 7, 9)
     check = sum(int(n) * weights[i % 4]
@@ -57,7 +59,7 @@ def calc_check_digit(number):
     return str((10 - check) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid CBU."""
     number = compact(number)
     if len(number) != 22:
@@ -71,7 +73,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid CBU."""
     try:
         return bool(validate(number))
@@ -79,7 +81,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join((number[:8], number[8:]))
diff --git a/stdnum/ar/cuit.py b/stdnum/ar/cuit.py
index 89522b2..2e461c8 100644
--- a/stdnum/ar/cuit.py
+++ b/stdnum/ar/cuit.py
@@ -41,17 +41,19 @@ InvalidChecksum: ...
 '20-26756539-3'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     weights = (5, 4, 3, 2, 7, 6, 5, 4, 3, 2)
     check = sum(w * int(n) for w, n in zip(weights, number)) % 11
@@ -66,7 +68,7 @@ _cuit_tpes = (
 )
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid CUIT."""
     number = compact(number)
     if len(number) != 11:
@@ -80,7 +82,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid CUIT."""
     try:
         return bool(validate(number))
@@ -88,7 +90,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return (number[0:2] + '-' + number[2:10] + '-' + number[10:])
diff --git a/stdnum/ar/dni.py b/stdnum/ar/dni.py
index f97cb7a..fbeb48a 100644
--- a/stdnum/ar/dni.py
+++ b/stdnum/ar/dni.py
@@ -38,17 +38,19 @@ InvalidLength: ...
 '20.123.456'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' .').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid DNI."""
     number = compact(number)
     if not isdigits(number):
@@ -58,7 +60,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid DNI."""
     try:
         return bool(validate(number))
@@ -66,7 +68,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '.'.join((number[:-6], number[-6:-3], number[-3:]))
diff --git a/stdnum/at/__init__.py b/stdnum/at/__init__.py
index 3ee90ad..46c3b97 100644
--- a/stdnum/at/__init__.py
+++ b/stdnum/at/__init__.py
@@ -20,6 +20,8 @@
 
 """Collection of Austrian numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.at import postleitzahl as postal_code  # noqa: F401
 from stdnum.at import uid as vat  # noqa: F401
diff --git a/stdnum/at/businessid.py b/stdnum/at/businessid.py
index 04fc564..45d8b54 100644
--- a/stdnum/at/businessid.py
+++ b/stdnum/at/businessid.py
@@ -38,6 +38,8 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -47,7 +49,7 @@ from stdnum.util import clean
 _businessid_re = re.compile('^[0-9]+[a-z]$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace.
     Preceding "FN" is also removed."""
@@ -57,7 +59,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid company register number. This only
     checks the formatting."""
     number = compact(number)
@@ -66,7 +68,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid company register number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/at/postleitzahl.py b/stdnum/at/postleitzahl.py
index feafa84..8577358 100644
--- a/stdnum/at/postleitzahl.py
+++ b/stdnum/at/postleitzahl.py
@@ -45,17 +45,19 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number).strip()
 
 
-def info(number):
+def info(number: str) -> dict[str, str]:
     """Return a dictionary of data about the supplied number. This typically
     returns the location."""
     number = compact(number)
@@ -63,7 +65,7 @@ def info(number):
     return numdb.get('at/postleitzahl').info(number)[0][1]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid postal code."""
     number = compact(number)
     if not isdigits(number):
@@ -75,7 +77,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid postal code."""
     try:
         return bool(validate(number))
diff --git a/stdnum/at/tin.py b/stdnum/at/tin.py
index 88f0037..26627b4 100644
--- a/stdnum/at/tin.py
+++ b/stdnum/at/tin.py
@@ -44,17 +44,19 @@ More information:
 '59-119/9013'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -./,').strip()
 
 
-def _min_fa(office):
+def _min_fa(office: str) -> str:
     """Convert the tax office name to something that we can use for
     comparison without running into encoding issues."""
     return ''.join(
@@ -62,7 +64,7 @@ def _min_fa(office):
         if x in 'bcdefghijklmnopqrstvwxyz')
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     number = compact(number)
     s = sum(
@@ -71,7 +73,7 @@ def calc_check_digit(number):
     return str((10 - s) % 10)
 
 
-def info(number):
+def info(number: str) -> dict[str, str]:
     """Return a dictionary of data about the supplied number. This typically
     returns the the tax office and region."""
     number = compact(number)
@@ -79,7 +81,7 @@ def info(number):
     return numdb.get('at/fa').info(number[:2])[0][1]
 
 
-def validate(number, office=None):
+def validate(number: str, office: str | None = None) -> str:
     """Check if the number is a valid tax identification number. This checks
     the length and formatting. The tax office can be supplied to check that
     the number was issued in the specified tax office."""
@@ -93,12 +95,12 @@ def validate(number, office=None):
     i = info(number)
     if not i:
         raise InvalidComponent()
-    if office and _min_fa(i.get('office')) != _min_fa(office):
+    if office and _min_fa(i.get('office', '')) != _min_fa(office):
         raise InvalidComponent()
     return number
 
 
-def is_valid(number, office=None):
+def is_valid(number: str, office: str | None = None) -> bool:
     """Check if the number is a valid tax identification number. This checks
     the length, formatting and check digit."""
     try:
@@ -107,7 +109,7 @@ def is_valid(number, office=None):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '%s-%s/%s' % (number[:2], number[2:5], number[5:])
diff --git a/stdnum/at/uid.py b/stdnum/at/uid.py
index 0ed537b..5e8b73e 100644
--- a/stdnum/at/uid.py
+++ b/stdnum/at/uid.py
@@ -32,12 +32,14 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -./').upper().strip()
@@ -46,13 +48,13 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     return str((6 - luhn.checksum(number[1:])) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -65,7 +67,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/at/vnr.py b/stdnum/at/vnr.py
index 73745a0..3b4862a 100644
--- a/stdnum/at/vnr.py
+++ b/stdnum/at/vnr.py
@@ -37,24 +37,26 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ')
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The fourth digit in the number is
     ignored."""
     weights = (3, 7, 9, 0, 5, 8, 4, 2, 1, 6)
     return str(sum(w * int(n) for w, n in zip(weights, number)) % 11)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -67,7 +69,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/au/__init__.py b/stdnum/au/__init__.py
index ba52276..906a9a6 100644
--- a/stdnum/au/__init__.py
+++ b/stdnum/au/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Australian numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.au import abn as vat  # noqa: F401
diff --git a/stdnum/au/abn.py b/stdnum/au/abn.py
index a0d4724..610e227 100644
--- a/stdnum/au/abn.py
+++ b/stdnum/au/abn.py
@@ -39,17 +39,19 @@ InvalidChecksum: ...
 '51 824 753 556'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip()
 
 
-def calc_check_digits(number):
+def calc_check_digits(number: str) -> str:
     """Calculate the check digits that should be prepended to make the number
     valid."""
     weights = (3, 5, 7, 9, 11, 13, 15, 17, 19)
@@ -57,7 +59,7 @@ def calc_check_digits(number):
     return str(11 + (s - 1) % 89)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid ABN. This checks the length, formatting
     and check digit."""
     number = compact(number)
@@ -70,7 +72,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid ABN."""
     try:
         return bool(validate(number))
@@ -78,7 +80,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join((number[0:2], number[2:5], number[5:8], number[8:]))
diff --git a/stdnum/au/acn.py b/stdnum/au/acn.py
index 77e6227..cee2ae7 100644
--- a/stdnum/au/acn.py
+++ b/stdnum/au/acn.py
@@ -41,22 +41,24 @@ InvalidChecksum: ...
 '43002724334'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the checksum."""
     return str((sum(int(n) * (i - 8) for i, n in enumerate(number))) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid ACN. This checks the length, formatting
     and check digit."""
     number = compact(number)
@@ -69,7 +71,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid ACN."""
     try:
         return bool(validate(number))
@@ -77,13 +79,13 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join((number[0:3], number[3:6], number[6:]))
 
 
-def to_abn(number):
+def to_abn(number: str) -> str:
     """Convert the number to an Australian Business Number (ABN)."""
     from stdnum.au import abn
     number = compact(number)
diff --git a/stdnum/au/tfn.py b/stdnum/au/tfn.py
index d515bac..33844d5 100644
--- a/stdnum/au/tfn.py
+++ b/stdnum/au/tfn.py
@@ -42,23 +42,25 @@ InvalidChecksum: ...
 '123 456 782'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip()
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum."""
     weights = (1, 4, 3, 7, 5, 8, 6, 9, 10)
     return sum(w * int(n) for w, n in zip(weights, number)) % 11
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid TFN. This checks the length, formatting
     and check digit."""
     number = compact(number)
@@ -71,7 +73,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid TFN."""
     try:
         return bool(validate(number))
@@ -79,7 +81,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join((number[0:3], number[3:6], number[6:]))
diff --git a/stdnum/be/__init__.py b/stdnum/be/__init__.py
index d131345..26ce6d5 100644
--- a/stdnum/be/__init__.py
+++ b/stdnum/be/__init__.py
@@ -20,6 +20,8 @@
 
 """Collection of Belgian numbers."""
 
+from __future__ import annotations
+
 # provide businessid as an alias
 from stdnum.be import nn as personalid  # noqa: F401
 from stdnum.be import vat as businessid  # noqa: F401
diff --git a/stdnum/be/bis.py b/stdnum/be/bis.py
index fc59cf5..20d0d6b 100644
--- a/stdnum/be/bis.py
+++ b/stdnum/be/bis.py
@@ -65,18 +65,22 @@ datetime.date(1998, 7, 28)
 'M'
 """
 
+from __future__ import annotations
+
+import datetime
+
 from stdnum.be import nn
 from stdnum.exceptions import *
 from stdnum.util import isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the number
     of any valid separators and removes surrounding whitespace."""
     return nn.compact(number)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid BIS Number."""
     number = compact(number)
     if not isdigits(number) or int(number) <= 0:
@@ -89,7 +93,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid BIS Number."""
     try:
         return bool(validate(number))
@@ -97,27 +101,27 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return nn.format(number)
 
 
-def get_birth_year(number):
+def get_birth_year(number: str) -> int | None:
     """Return the year of the birth date."""
     return nn.get_birth_year(number)
 
 
-def get_birth_month(number):
+def get_birth_month(number: str) -> int | None:
     """Return the month of the birth date."""
     return nn.get_birth_month(number)
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date | None:
     """Return the date of birth."""
     return nn.get_birth_date(number)
 
 
-def get_gender(number):
+def get_gender(number: str) -> str | None:
     """Get the person's gender ('M' or 'F'), which for BIS
     numbers is only known if the month was incremented with 40."""
     number = compact(number)
diff --git a/stdnum/be/eid.py b/stdnum/be/eid.py
index 96a5212..4c395d6 100644
--- a/stdnum/be/eid.py
+++ b/stdnum/be/eid.py
@@ -54,17 +54,19 @@ InvalidChecksum: ...
 '591-1917064-58'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -./').upper().strip()
 
 
-def _calc_check_digits(number):
+def _calc_check_digits(number: str) -> str:
     """Calculate the expected check digits for the number, calculated as
     the remainder of dividing the first 10 digits of the number by 97.
     If the remainder is 0, the check number is set to 97.
@@ -72,7 +74,7 @@ def _calc_check_digits(number):
     return '%02d' % ((int(number[:10]) % 97) or 97)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid ID card number.
     This checks the length, formatting and check digit."""
     number = compact(number)
@@ -85,7 +87,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Belgian ID Card number."""
     try:
         return bool(validate(number))
@@ -93,7 +95,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join((number[:3], number[3:10], number[10:]))
diff --git a/stdnum/be/iban.py b/stdnum/be/iban.py
index 83c3669..98adc12 100644
--- a/stdnum/be/iban.py
+++ b/stdnum/be/iban.py
@@ -47,6 +47,8 @@ InvalidComponent: ...
 True
 """
 
+from __future__ import annotations
+
 from stdnum import iban
 from stdnum.exceptions import *
 
@@ -58,13 +60,13 @@ compact = iban.compact
 format = iban.format
 
 
-def _calc_check_digits(number):
+def _calc_check_digits(number: str) -> str:
     """Calculate the check digits over the provided part of the number."""
     check = int(number) % 97
     return '%02d' % (check or 97)
 
 
-def info(number):
+def info(number: str) -> dict[str, str]:
     """Return a dictionary of data about the supplied number. This typically
     returns the name of the bank and a BIC if it is valid."""
     number = compact(number)
@@ -72,12 +74,12 @@ def info(number):
     return numdb.get('be/banks').info(number[4:7])[0][1]
 
 
-def to_bic(number):
+def to_bic(number: str) -> str | None:
     """Return the BIC for the bank that this number refers to."""
     return info(number).get('bic')
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid Belgian IBAN."""
     number = iban.validate(number, check_country=False)
     if not number.startswith('BE'):
@@ -89,7 +91,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid Belgian IBAN."""
     try:
         return bool(validate(number))
diff --git a/stdnum/be/nn.py b/stdnum/be/nn.py
index 6cf64af..8d08e15 100644
--- a/stdnum/be/nn.py
+++ b/stdnum/be/nn.py
@@ -82,6 +82,8 @@ datetime.date(1985, 7, 30)
 'M'
 """
 
+from __future__ import annotations
+
 import calendar
 import datetime
 
@@ -89,14 +91,14 @@ from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the number
     of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -.').strip()
     return number
 
 
-def _checksum(number):
+def _checksum(number: str) -> int:
     """Calculate the checksum and return the detected century."""
     numbers = [number]
     if int(number[:2]) + 2000 <= datetime.date.today().year:
@@ -107,7 +109,7 @@ def _checksum(number):
     raise InvalidChecksum()
 
 
-def _get_birth_date_parts(number):
+def _get_birth_date_parts(number: str) -> tuple[int | None, int | None, int | 
None]:
     """Check if the number's encoded birth date is valid, and return the 
contained
     birth year, month and day of month, accounting for unknown values."""
     century = _checksum(number)
@@ -138,7 +140,7 @@ def _get_birth_date_parts(number):
     return (year, month, day)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid National Number."""
     number = compact(number)
     if not isdigits(number) or int(number) <= 0:
@@ -151,7 +153,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid National Number."""
     try:
         return bool(validate(number))
@@ -159,7 +161,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return (
@@ -167,19 +169,19 @@ def format(number):
         '-' + '.'.join([number[6:9], number[9:11]]))
 
 
-def get_birth_year(number):
+def get_birth_year(number: str) -> int | None:
     """Return the year of the birth date."""
     year, month, day = _get_birth_date_parts(compact(number))
     return year
 
 
-def get_birth_month(number):
+def get_birth_month(number: str) -> int | None:
     """Return the month of the birth date."""
     year, month, day = _get_birth_date_parts(compact(number))
     return month
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date | None:
     """Return the date of birth."""
     year, month, day = _get_birth_date_parts(compact(number))
     if year and month and day:
@@ -187,7 +189,7 @@ def get_birth_date(number):
     return None
 
 
-def get_gender(number):
+def get_gender(number: str) -> str:
     """Get the person's gender ('M' or 'F')."""
     number = compact(number)
     if int(number[6:9]) % 2:
diff --git a/stdnum/be/ssn.py b/stdnum/be/ssn.py
index e22cfd8..c51f876 100644
--- a/stdnum/be/ssn.py
+++ b/stdnum/be/ssn.py
@@ -89,6 +89,10 @@ datetime.date(1998, 7, 28)
 
 """
 
+from __future__ import annotations
+
+import datetime
+
 from stdnum.be import bis, nn
 from stdnum.exceptions import *
 
@@ -96,13 +100,13 @@ from stdnum.exceptions import *
 _ssn_modules = (nn, bis)
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return nn.compact(number)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Belgian SSN. This searches for
     the proper sub-type and validates using that."""
     try:
@@ -113,7 +117,7 @@ def validate(number):
         return nn.validate(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Belgian SSN number."""
     try:
         return bool(validate(number))
@@ -121,7 +125,7 @@ def is_valid(number):
         return False
 
 
-def guess_type(number):
+def guess_type(number: str) -> str | None:
     """Return the Belgian SSN type for which this number is valid."""
     for mod in _ssn_modules:
         if mod.is_valid(number):
@@ -129,29 +133,29 @@ def guess_type(number):
     return None
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return nn.format(number)
 
 
-def get_birth_year(number):
+def get_birth_year(number: str) -> int | None:
     """Return the year of the birth date."""
     return nn.get_birth_year(number)
 
 
-def get_birth_month(number):
+def get_birth_month(number: str) -> int | None:
     """Return the month of the birth date."""
     return nn.get_birth_month(number)
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date | None:
     """Return the date of birth."""
     return nn.get_birth_date(number)
 
 
-def get_gender(number):
+def get_gender(number: str) -> str | None:
     """Get the person's gender ('M' or 'F')."""
     for mod in _ssn_modules:
         if mod.is_valid(number):
-            return mod.get_gender(number)
+            return mod.get_gender(number)  # type: ignore[no-any-return]
     return None
diff --git a/stdnum/be/vat.py b/stdnum/be/vat.py
index 1722022..d4c235b 100644
--- a/stdnum/be/vat.py
+++ b/stdnum/be/vat.py
@@ -35,11 +35,13 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -./').upper().strip()
@@ -52,12 +54,12 @@ def compact(number):
     return number
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum."""
     return (int(number[:-2]) + int(number[-2:])) % 97
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -70,7 +72,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/bg/egn.py b/stdnum/bg/egn.py
index 9591182..30daf44 100644
--- a/stdnum/bg/egn.py
+++ b/stdnum/bg/egn.py
@@ -41,26 +41,28 @@ Traceback (most recent call last):
 InvalidComponent: ...
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -.').upper().strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     weights = (2, 4, 8, 5, 10, 9, 7, 3, 6)
     return str(sum(w * int(n) for w, n in zip(weights, number)) % 11 % 10)
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Split the date parts from the number and return the birth date."""
     number = compact(number)
     year = int(number[0:2]) + 1900
@@ -78,7 +80,7 @@ def get_birth_date(number):
         raise InvalidComponent()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid national identification number. This
     checks the length, formatting, embedded date and check digit."""
     number = compact(number)
@@ -95,7 +97,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid national identification number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/bg/pnf.py b/stdnum/bg/pnf.py
index 9486371..2981c7a 100644
--- a/stdnum/bg/pnf.py
+++ b/stdnum/bg/pnf.py
@@ -35,24 +35,26 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -.').upper().strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     weights = (21, 19, 17, 13, 11, 9, 7, 3, 1)
     return str(sum(w * int(n) for w, n in zip(weights, number)) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid national identification number. This
     checks the length, formatting, embedded date and check digit."""
     number = compact(number)
@@ -65,7 +67,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid national identification number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/bg/vat.py b/stdnum/bg/vat.py
index c3717c7..001f434 100644
--- a/stdnum/bg/vat.py
+++ b/stdnum/bg/vat.py
@@ -34,12 +34,14 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.bg import egn, pnf
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -.').upper().strip()
@@ -48,7 +50,7 @@ def compact(number):
     return number
 
 
-def calc_check_digit_legal(number):
+def calc_check_digit_legal(number: str) -> str:
     """Calculate the check digit for legal entities. The number passed
     should not have the check digit included."""
     check = sum((i + 1) * int(n) for i, n in enumerate(number)) % 11
@@ -57,14 +59,14 @@ def calc_check_digit_legal(number):
     return str(check % 10)
 
 
-def calc_check_digit_other(number):
+def calc_check_digit_other(number: str) -> str:
     """Calculate the check digit for others. The number passed should not
     have the check digit included."""
     weights = (4, 3, 2, 7, 6, 5, 4, 3, 2)
     return str((11 - sum(w * int(n) for w, n in zip(weights, number))) % 11)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -84,7 +86,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/bic.py b/stdnum/bic.py
index a4f2a50..54b311c 100644
--- a/stdnum/bic.py
+++ b/stdnum/bic.py
@@ -47,6 +47,8 @@ InvalidFormat: ..
 
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -56,13 +58,13 @@ from stdnum.util import clean
 _bic_re = re.compile(r'^[A-Z]{6}[0-9A-Z]{2}([0-9A-Z]{3})?$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any surrounding whitespace."""
     return clean(number, ' -').strip().upper()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid routing number. This checks the length
     and characters in each position."""
     number = compact(number)
@@ -74,7 +76,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid BIC."""
     try:
         return bool(validate(number))
@@ -82,6 +84,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/bitcoin.py b/stdnum/bitcoin.py
index 6fffa26..6e34d02 100644
--- a/stdnum/bitcoin.py
+++ b/stdnum/bitcoin.py
@@ -42,15 +42,18 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 import hashlib
 import struct
+from collections.abc import Iterable
 from functools import reduce
 
 from stdnum.exceptions import *
 from stdnum.util import clean
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' ').strip()
@@ -63,7 +66,7 @@ def compact(number):
 _base58_alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
 
 
-def b58decode(s):
+def b58decode(s: str) -> bytes:
     """Decode a Base58 encoded string to a bytestring."""
     value = reduce(lambda a, c: a * 58 + _base58_alphabet.index(c), s, 0)
     result = b''
@@ -84,18 +87,18 @@ _bech32_generator = (
     (1 << 3, 0x3d4233dd), (1 << 4, 0x2a1462b3))
 
 
-def bech32_checksum(values):
+def bech32_checksum(values: Iterable[int]) -> int:
     """Calculate the Bech32 checksum."""
     chk = 1
     for value in values:
         top = chk >> 25
         chk = (chk & 0x1ffffff) << 5 | value
-        for t, v in _bech32_generator:
-            chk ^= v if top & t else 0
+        for test, val in _bech32_generator:
+            chk ^= val if top & test else 0
     return chk
 
 
-def b32decode(data):
+def b32decode(data: Iterable[int]) -> bytes:
     """Decode a list of Base32 values to a bytestring."""
     acc, bits = 0, 0
     result = b''
@@ -110,12 +113,12 @@ def b32decode(data):
     return result
 
 
-def _expand_hrp(hrp):
+def _expand_hrp(hrp: str) -> list[int]:
     """Convert the human-readable part to format for checksum calculation."""
     return [ord(c) >> 5 for c in hrp] + [0] + [ord(c) & 31 for c in hrp]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is valid. This checks the length and
     check digit."""
     number = compact(number)
@@ -150,7 +153,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is valid. This checks the length and
     check digit."""
     try:
diff --git a/stdnum/br/__init__.py b/stdnum/br/__init__.py
index 3b2d22f..2838f9b 100644
--- a/stdnum/br/__init__.py
+++ b/stdnum/br/__init__.py
@@ -19,4 +19,7 @@
 # 02110-1301 USA
 
 """Collection of Brazilian numbers."""
+
+from __future__ import annotations
+
 from stdnum.br import cnpj as vat  # noqa: F401
diff --git a/stdnum/br/cnpj.py b/stdnum/br/cnpj.py
index e2fbcdb..3178bec 100644
--- a/stdnum/br/cnpj.py
+++ b/stdnum/br/cnpj.py
@@ -38,17 +38,19 @@ InvalidFormat: ...
 '16.727.230/0001-97'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -./').strip()
 
 
-def calc_check_digits(number):
+def calc_check_digits(number: str) -> str:
     """Calculate the check digits for the number."""
     d1 = (11 - sum(((3 - i) % 8 + 2) * int(n)
                    for i, n in enumerate(number[:12]))) % 11 % 10
@@ -58,7 +60,7 @@ def calc_check_digits(number):
     return '%d%d' % (d1, d2)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid CNPJ. This checks the length and
     whether the check digits are correct."""
     number = compact(number)
@@ -71,7 +73,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid CNPJ."""
     try:
         return bool(validate(number))
@@ -79,7 +81,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return (number[0:2] + '.' + number[2:5] + '.' + number[5:8] + '/' +
diff --git a/stdnum/br/cpf.py b/stdnum/br/cpf.py
index d0e5dbb..554982f 100644
--- a/stdnum/br/cpf.py
+++ b/stdnum/br/cpf.py
@@ -42,17 +42,19 @@ InvalidFormat: ...
 '231.002.999-00'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -.').strip()
 
 
-def _calc_check_digits(number):
+def _calc_check_digits(number: str) -> str:
     """Calculate the check digits for the number."""
     d1 = sum((10 - i) * int(number[i]) for i in range(9))
     d1 = (11 - d1) % 11 % 10
@@ -61,7 +63,7 @@ def _calc_check_digits(number):
     return '%d%d' % (d1, d2)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid CPF. This checks the length and whether
     the check digit is correct."""
     number = compact(number)
@@ -74,7 +76,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid CPF."""
     try:
         return bool(validate(number))
@@ -82,7 +84,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return number[:3] + '.' + number[3:6] + '.' + number[6:-2] + '-' + 
number[-2:]
diff --git a/stdnum/by/__init__.py b/stdnum/by/__init__.py
index 893f386..529c0ac 100644
--- a/stdnum/by/__init__.py
+++ b/stdnum/by/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Belarusian numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.by import unp as vat  # noqa: F401
diff --git a/stdnum/by/unp.py b/stdnum/by/unp.py
index b9c7ebc..31ad3bf 100644
--- a/stdnum/by/unp.py
+++ b/stdnum/by/unp.py
@@ -41,6 +41,8 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
@@ -52,7 +54,7 @@ _cyrillic_to_latin = dict(zip(
 ))
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' ').upper().strip()
@@ -63,7 +65,7 @@ def compact(number):
     return ''.join(_cyrillic_to_latin.get(x, x) for x in number)
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for the number."""
     number = compact(number)
     alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
@@ -76,7 +78,7 @@ def calc_check_digit(number):
     return str(c)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -93,7 +95,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid number."""
     try:
         return bool(validate(number))
@@ -101,7 +103,11 @@ def is_valid(number):
         return False
 
 
-def check_nalog(number, timeout=30, verify=True):  # pragma: no cover (not 
part of normal test suite)
+def check_nalog(
+    number: str,
+    timeout: float = 30,
+    verify: bool | str = True,
+) -> dict[str, str | None] | None:  # pragma: no cover (not part of normal 
test suite)
     """Retrieve registration information from the portal.nalog.gov.by web site.
 
     The `timeout` argument specifies the network timeout in seconds.
@@ -127,4 +133,5 @@ def check_nalog(number, timeout=30, verify=True):  # 
pragma: no cover (not part
         timeout=timeout,
         verify=verify)
     if response.ok and response.content:
-        return response.json()['row']
+        return response.json()['row']  # type: ignore[no-any-return]
+    return None
diff --git a/stdnum/ca/__init__.py b/stdnum/ca/__init__.py
index 5d44d1e..8518396 100644
--- a/stdnum/ca/__init__.py
+++ b/stdnum/ca/__init__.py
@@ -19,4 +19,7 @@
 # 02110-1301 USA
 
 """Collection of Canadian numbers."""
+
+from __future__ import annotations
+
 from stdnum.ca import bn as vat  # noqa: F401
diff --git a/stdnum/ca/bc_phn.py b/stdnum/ca/bc_phn.py
index e2cfa67..56ba49a 100644
--- a/stdnum/ca/bc_phn.py
+++ b/stdnum/ca/bc_phn.py
@@ -55,18 +55,19 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """  # noqa: E501
 
+from __future__ import annotations
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, '- ').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the check
     digit included."""
     weights = (2, 4, 8, 5, 10, 9, 7, 3)
@@ -74,7 +75,7 @@ def calc_check_digit(number):
     return str((11 - s) % 11)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid PHN. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -89,7 +90,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid PHN."""
     try:
         return bool(validate(number))
@@ -97,7 +98,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join((number[0:4], number[4:7], number[7:]))
diff --git a/stdnum/ca/bn.py b/stdnum/ca/bn.py
index 0345d83..1f60c01 100644
--- a/stdnum/ca/bn.py
+++ b/stdnum/ca/bn.py
@@ -43,18 +43,20 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, '- ').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid BN or BN15. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -71,7 +73,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid BN or BN15."""
     try:
         return bool(validate(number))
diff --git a/stdnum/ca/sin.py b/stdnum/ca/sin.py
index 102b57e..6e0f0fe 100644
--- a/stdnum/ca/sin.py
+++ b/stdnum/ca/sin.py
@@ -47,18 +47,20 @@ InvalidComponent: ...
 '123-456-782'
 """
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, '- ').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid SIN. This checks the length, formatting
     and check digit."""
     number = compact(number)
@@ -71,7 +73,7 @@ def validate(number):
     return luhn.validate(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid SIN."""
     try:
         return bool(validate(number))
@@ -79,7 +81,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join((number[0:3], number[3:6], number[6:]))
diff --git a/stdnum/casrn.py b/stdnum/casrn.py
index f4f1dc6..3eb850a 100644
--- a/stdnum/casrn.py
+++ b/stdnum/casrn.py
@@ -38,6 +38,8 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -47,7 +49,7 @@ from stdnum.util import clean
 _cas_re = re.compile(r'^[1-9][0-9]{1,6}-[0-9]{2}-[0-9]$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation."""
     number = clean(number, ' ').strip()
     if '-' not in number:
@@ -55,7 +57,7 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for the number. The passed number should not
     have the check digit included."""
     number = number.replace('-', '')
@@ -63,7 +65,7 @@ def calc_check_digit(number):
         sum((i + 1) * int(n) for i, n in enumerate(reversed(number))) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid CAS RN."""
     number = compact(number)
     if not 7 <= len(number) <= 12:
@@ -75,7 +77,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid CAS RN."""
     try:
         return bool(validate(number))
diff --git a/stdnum/cfi.py b/stdnum/cfi.py
index f4ffeaa..492bb54 100644
--- a/stdnum/cfi.py
+++ b/stdnum/cfi.py
@@ -47,6 +47,8 @@ InvalidComponent: ...
 }
 """
 
+from __future__ import annotations
+
 from stdnum import numdb
 from stdnum.exceptions import *
 from stdnum.util import clean
@@ -56,13 +58,13 @@ from stdnum.util import clean
 _cfidb = numdb.get('cfi')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip().upper()
 
 
-def info(number):
+def info(number: str) -> dict[str, str]:
     """Look up information about the number."""
     number = compact(number)
     info = _cfidb.info(number)
@@ -79,7 +81,7 @@ def info(number):
     return properties
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is valid. This checks the length and
     format."""
     number = compact(number)
@@ -91,7 +93,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is valid. This checks the length and
     check digit."""
     try:
diff --git a/stdnum/ch/esr.py b/stdnum/ch/esr.py
index 8d62380..0b94fdf 100644
--- a/stdnum/ch/esr.py
+++ b/stdnum/ch/esr.py
@@ -49,17 +49,19 @@ InvalidChecksum: ...
 '00 00000 00000 00000 00018 78583'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips
     surrounding whitespace and separators."""
     return clean(number, ' ').lstrip('0')
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for number. The number passed should
     not have the check digit included."""
     _digits = (0, 9, 4, 6, 8, 2, 7, 1, 3, 5)
@@ -69,7 +71,7 @@ def calc_check_digit(number):
     return str((10 - c) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid ESR. This checks the length, formatting
     and check digit."""
     number = compact(number)
@@ -82,7 +84,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid ESR."""
     try:
         return bool(validate(number))
@@ -90,7 +92,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number).zfill(27)
     return number[:2] + ' ' + ' '.join(
diff --git a/stdnum/ch/ssn.py b/stdnum/ch/ssn.py
index a59dc4f..5a1ddef 100644
--- a/stdnum/ch/ssn.py
+++ b/stdnum/ch/ssn.py
@@ -46,24 +46,26 @@ InvalidComponent: ...
 '756.9217.0769.85'
 """
 
+from __future__ import annotations
+
 from stdnum import ean
 from stdnum.exceptions import *
 from stdnum.util import clean
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' .').strip()
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '.'.join((number[:3], number[3:7], number[7:11], number[11:]))
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Swiss Sozialversicherungsnummer."""
     number = compact(number)
     if len(number) != 13:
@@ -73,7 +75,7 @@ def validate(number):
     return ean.validate(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Swiss Sozialversicherungsnummer."""
     try:
         return bool(validate(number))
diff --git a/stdnum/ch/uid.py b/stdnum/ch/uid.py
index 97a0f5f..5831817 100644
--- a/stdnum/ch/uid.py
+++ b/stdnum/ch/uid.py
@@ -46,11 +46,18 @@ InvalidChecksum: ...
 'CHE-100.155.212'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, get_soap_client, isdigits
 
 
-def compact(number):
+TYPE_CHECKING = False
+if TYPE_CHECKING:  # pragma: no cover (typechecking only import)
+    from typing import Any
+
+
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips
     surrounding whitespace and separators."""
     number = clean(number, ' -.').strip().upper()
@@ -59,7 +66,7 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for organisations. The number passed should
     not have the check digit included."""
     weights = (5, 4, 3, 2, 7, 6, 5, 4)
@@ -67,7 +74,7 @@ def calc_check_digit(number):
     return str((11 - s) % 11)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid UID. This checks the length, formatting
     and check digit."""
     number = compact(number)
@@ -82,7 +89,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid UID."""
     try:
         return bool(validate(number))
@@ -90,7 +97,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return number[:3] + '-' + '.'.join(
@@ -100,7 +107,11 @@ def format(number):
 uid_wsdl = 'https://www.uid-wse.admin.ch/V5.0/PublicServices.svc?wsdl'
 
 
-def check_uid(number, timeout=30, verify=True):  # pragma: no cover
+def check_uid(
+    number: str,
+    timeout: float = 30,
+    verify: bool | str = True,
+) -> dict[str, Any] | None:  # pragma: no cover
     """Look up information via the Swiss Federal Statistical Office web 
service.
 
     This uses the UID registry web service run by the the Swiss Federal
@@ -153,8 +164,10 @@ def check_uid(number, timeout=30, verify=True):  # pragma: 
no cover
     number = compact(number)
     client = get_soap_client(uid_wsdl, timeout=timeout, verify=verify)
     try:
-        return client.GetByUID(uid={'uidOrganisationIdCategorie': number[:3], 
'uidOrganisationId': number[3:]})[0]
+        return client.GetByUID(  # type: ignore[no-any-return]
+            uid={'uidOrganisationIdCategorie': number[:3], 
'uidOrganisationId': number[3:]},
+        )[0]
     except Exception:  # noqa: B902 (exception type depends on SOAP client)
         # Error responses by the server seem to result in exceptions raised
         # by the SOAP client implementation
-        return
+        return None
diff --git a/stdnum/ch/vat.py b/stdnum/ch/vat.py
index d6501f6..c66264c 100644
--- a/stdnum/ch/vat.py
+++ b/stdnum/ch/vat.py
@@ -43,17 +43,19 @@ InvalidChecksum: ...
 'CHE-107.787.577 IVA'
 """
 
+from __future__ import annotations
+
 from stdnum.ch import uid
 from stdnum.exceptions import *
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips
     surrounding whitespace and separators."""
     return uid.compact(number)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -65,7 +67,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
@@ -73,7 +75,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return uid.format(number[:12]) + ' ' + number[12:]
diff --git a/stdnum/cl/__init__.py b/stdnum/cl/__init__.py
index f31595e..6c88f78 100644
--- a/stdnum/cl/__init__.py
+++ b/stdnum/cl/__init__.py
@@ -20,6 +20,9 @@
 
 """Collection of Chilean numbers."""
 
+from __future__ import annotations
+
+
 # provide vat and run as an alias
 from stdnum.cl import rut as vat  # noqa: F401, isort:skip
 from stdnum.cl import rut as run  # noqa: F401, isort:skip
diff --git a/stdnum/cl/rut.py b/stdnum/cl/rut.py
index d15dba4..8a06a28 100644
--- a/stdnum/cl/rut.py
+++ b/stdnum/cl/rut.py
@@ -42,11 +42,13 @@ InvalidFormat: ...
 '12.531.909-2'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -.').upper().strip()
@@ -55,14 +57,14 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     s = sum(int(n) * (4 + (5 - i) % 6) for i, n in enumerate(number[::-1]))
     return '0123456789K'[s % 11]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid RUT. This checks the length, formatting
     and check digit."""
     number = compact(number)
@@ -75,7 +77,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid RUT."""
     try:
         return bool(validate(number))
@@ -83,7 +85,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return (number[:-7] + '.' + number[-7:-4] + '.' +
diff --git a/stdnum/cn/__init__.py b/stdnum/cn/__init__.py
index 93a6f5e..86235cc 100644
--- a/stdnum/cn/__init__.py
+++ b/stdnum/cn/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of China (PRC) numbers."""
 
+from __future__ import annotations
+
 # Provide vat as an alias.
 from stdnum.cn import uscc as vat  # noqa: F401
diff --git a/stdnum/cn/ric.py b/stdnum/cn/ric.py
index df70c06..4cbe486 100644
--- a/stdnum/cn/ric.py
+++ b/stdnum/cn/ric.py
@@ -32,19 +32,21 @@ digit is the checksum.
 '360426199101010071'
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number).upper().strip()
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Split the date parts from the number and return the birth date.
     Note that in some cases it may return the registration date instead of
     the birth date and it may be a century off."""
@@ -58,7 +60,7 @@ def get_birth_date(number):
         raise InvalidComponent()
 
 
-def get_birth_place(number):
+def get_birth_place(number: str) -> dict[str, str]:
     """Use the number to look up the place of birth of the person."""
     from stdnum import numdb
     number = compact(number)
@@ -68,14 +70,14 @@ def get_birth_place(number):
     return results
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should have the check
     digit included."""
     checksum = (1 - 2 * int(number[:-1], 13)) % 11
     return 'X' if checksum == 10 else str(checksum)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid RIC number. This checks the length,
     formatting and birth date and place."""
     number = compact(number)
@@ -90,7 +92,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid RIC number."""
     try:
         return bool(validate(number))
@@ -98,6 +100,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/cn/uscc.py b/stdnum/cn/uscc.py
index a04a553..7f870c2 100644
--- a/stdnum/cn/uscc.py
+++ b/stdnum/cn/uscc.py
@@ -71,6 +71,8 @@ InvalidLength: ...
 '91110000600037341L'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
@@ -78,7 +80,7 @@ from stdnum.util import clean, isdigits
 _alphabet = '0123456789ABCDEFGHJKLMNPQRTUWXY'
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -87,7 +89,7 @@ def compact(number):
     return clean(number, ' -').upper().strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for the number."""
     weights = (1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28)
     number = compact(number)
@@ -95,7 +97,7 @@ def calc_check_digit(number):
     return _alphabet[(31 - total) % 31]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid USCC.
 
     This checks the length, formatting and check digit.
@@ -112,7 +114,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid USCC."""
     try:
         return bool(validate(number))
@@ -120,6 +122,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/co/__init__.py b/stdnum/co/__init__.py
index de40a0c..320cab0 100644
--- a/stdnum/co/__init__.py
+++ b/stdnum/co/__init__.py
@@ -20,6 +20,9 @@
 
 """Collection of Colombian numbers."""
 
+from __future__ import annotations
+
+
 # provide vat and rut as an alias
 from stdnum.co import nit as vat  # noqa: F401, isort:skip
 from stdnum.co import nit as rut  # noqa: F401, isort:skip
diff --git a/stdnum/co/nit.py b/stdnum/co/nit.py
index 2b18205..f250584 100644
--- a/stdnum/co/nit.py
+++ b/stdnum/co/nit.py
@@ -35,17 +35,19 @@ InvalidChecksum: ...
 '213.123.432-1'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips
     surrounding whitespace and separation dash."""
     return clean(number, '.,- ').upper().strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     weights = (3, 7, 13, 17, 19, 23, 29, 37, 41, 43, 47, 53, 59, 67, 71)
@@ -53,7 +55,7 @@ def calc_check_digit(number):
     return '01987654321'[s]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid NIT. This checks the length, formatting
     and check digit."""
     number = compact(number)
@@ -66,7 +68,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid NIT."""
     try:
         return bool(validate(number))
@@ -74,7 +76,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '.'.join(
diff --git a/stdnum/cr/__init__.py b/stdnum/cr/__init__.py
index d9b149a..9cc8e3d 100644
--- a/stdnum/cr/__init__.py
+++ b/stdnum/cr/__init__.py
@@ -19,4 +19,7 @@
 # 02110-1301 USA
 
 """Collection of Costa Rican numbers."""
+
+from __future__ import annotations
+
 from stdnum.cr import cpj as vat  # noqa: F401
diff --git a/stdnum/cr/cpf.py b/stdnum/cr/cpf.py
index 1d09887..ff90626 100644
--- a/stdnum/cr/cpf.py
+++ b/stdnum/cr/cpf.py
@@ -53,11 +53,13 @@ InvalidLength: ...
 '01-0613-0584'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -76,7 +78,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Costa Rica CPF number.
 
     This checks the length and formatting.
@@ -91,7 +93,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Costa Rica CPF number."""
     try:
         return bool(validate(number))
@@ -99,7 +101,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join([number[:2], number[2:6], number[6:]])
diff --git a/stdnum/cr/cpj.py b/stdnum/cr/cpj.py
index 2df838b..30845c1 100644
--- a/stdnum/cr/cpj.py
+++ b/stdnum/cr/cpj.py
@@ -49,11 +49,13 @@ InvalidLength: ...
 '4-000-042138'
 """  # noqa: E501
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -62,7 +64,7 @@ def compact(number):
     return clean(number, ' -').upper().strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Costa Rica CPJ number.
 
     This checks the length and formatting.
@@ -89,7 +91,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Costa Rica CPJ number."""
     try:
         return bool(validate(number))
@@ -97,7 +99,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join([number[0], number[1:4], number[4:]])
diff --git a/stdnum/cr/cr.py b/stdnum/cr/cr.py
index a63e414..13fb71f 100644
--- a/stdnum/cr/cr.py
+++ b/stdnum/cr/cr.py
@@ -51,11 +51,13 @@ InvalidLength: ...
 '122200569906'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -64,7 +66,7 @@ def compact(number):
     return clean(number, ' -').upper().strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Costa Rica CR number.
 
     This checks the length and formatting.
@@ -79,7 +81,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Costa Rica CR number."""
     try:
         return bool(validate(number))
@@ -87,6 +89,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/cu/ni.py b/stdnum/cu/ni.py
index 2202e68..3956d52 100644
--- a/stdnum/cu/ni.py
+++ b/stdnum/cu/ni.py
@@ -50,19 +50,21 @@ Traceback (most recent call last):
 InvalidComponent: ...
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips
     surrounding whitespace and separation dash."""
     return clean(number, ' ').strip()
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Split the date parts from the number and return the date of birth."""
     number = compact(number)
     year = int(number[0:2])
@@ -80,7 +82,7 @@ def get_birth_date(number):
         raise InvalidComponent()
 
 
-def get_gender(number):
+def get_gender(number: str) -> str:
     """Get the gender (M/F) from the person's NI."""
     number = compact(number)
     if int(number[9]) % 2:
@@ -89,7 +91,7 @@ def get_gender(number):
         return 'M'
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid NI. This checks the length, formatting
     and check digit."""
     number = compact(number)
@@ -101,7 +103,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid NI."""
     try:
         return bool(validate(number))
diff --git a/stdnum/cusip.py b/stdnum/cusip.py
index feaad0c..a560e54 100644
--- a/stdnum/cusip.py
+++ b/stdnum/cusip.py
@@ -39,11 +39,13 @@ InvalidChecksum: ...
 'US91324PAE25'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip().upper()
@@ -52,7 +54,7 @@ def compact(number):
 _alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*@#'
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digits for the number."""
     # convert to numeric first, then sum individual digits
     number = ''.join(
@@ -60,7 +62,7 @@ def calc_check_digit(number):
     return str((10 - sum(int(n) for n in number)) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is valid. This checks the length and
     check digit."""
     number = compact(number)
@@ -73,7 +75,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is valid. This checks the length and
     check digit."""
     try:
@@ -82,7 +84,7 @@ def is_valid(number):
         return False
 
 
-def to_isin(number):
+def to_isin(number: str) -> str:
     """Convert the number to an ISIN."""
     from stdnum import isin
     return isin.from_natid('US', number)
diff --git a/stdnum/cy/vat.py b/stdnum/cy/vat.py
index ce9115c..5862134 100644
--- a/stdnum/cy/vat.py
+++ b/stdnum/cy/vat.py
@@ -33,11 +33,13 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').upper().strip()
@@ -46,7 +48,7 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     translation = {
@@ -59,7 +61,7 @@ def calc_check_digit(number):
     ) % 26]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -74,7 +76,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/cz/__init__.py b/stdnum/cz/__init__.py
index fc74901..6e292b5 100644
--- a/stdnum/cz/__init__.py
+++ b/stdnum/cz/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Czech numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.cz import dic as vat  # noqa: F401
diff --git a/stdnum/cz/bankaccount.py b/stdnum/cz/bankaccount.py
index 92c2c6e..4fbc59b 100644
--- a/stdnum/cz/bankaccount.py
+++ b/stdnum/cz/bankaccount.py
@@ -48,6 +48,8 @@ InvalidComponent: ...
 'KOMBCZPP'
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -58,7 +60,7 @@ _bankaccount_re = re.compile(
     r'((?P<prefix>[0-9]{0,6})-)?(?P<root>[0-9]{2,10})\/(?P<bank>[0-9]{4})')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number).strip()
@@ -71,7 +73,7 @@ def compact(number):
     return number
 
 
-def _split(number):
+def _split(number: str) -> tuple[str | None, str, str]:
     """Split valid numbers into prefix, root and bank parts of the number."""
     match = _bankaccount_re.match(number)
     if not match:
@@ -79,7 +81,7 @@ def _split(number):
     return match.group('prefix'), match.group('root'), match.group('bank')
 
 
-def _info(bank):
+def _info(bank: str) -> dict[str, str]:
     """Look up information for the bank."""
     from stdnum import numdb
     info = {}
@@ -88,27 +90,29 @@ def _info(bank):
     return info
 
 
-def info(number):
+def info(number: str) -> dict[str, str]:
     """Return a dictionary of data about the supplied number. This typically
     returns the name of the bank and branch and a BIC if it is valid."""
     prefix, root, bank = _split(compact(number))
     return _info(bank)
 
 
-def to_bic(number):
+def to_bic(number: str) -> str | None:
     """Return the BIC for the bank that this number refers to."""
     return info(number).get('bic')
 
 
-def _calc_checksum(number):
+def _calc_checksum(number: str) -> int:
     weights = (6, 3, 7, 9, 10, 5, 8, 4, 2, 1)
     return sum(w * int(n) for w, n in zip(weights, number.zfill(10))) % 11
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid bank account number."""
     number = compact(number)
     prefix, root, bank = _split(number)
+    # guaranteed to be present because compacts adds a missing prefix
+    assert prefix
     if _calc_checksum(prefix) != 0:
         raise InvalidChecksum()
     if _calc_checksum(root) != 0:
@@ -118,7 +122,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid bank account number."""
     try:
         return bool(validate(number))
@@ -126,6 +130,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/cz/dic.py b/stdnum/cz/dic.py
index 475521d..b4b790b 100644
--- a/stdnum/cz/dic.py
+++ b/stdnum/cz/dic.py
@@ -40,12 +40,14 @@ InvalidChecksum: ...
 '640903926'
 """
 
+from __future__ import annotations
+
 from stdnum.cz import rc
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' /').upper().strip()
@@ -54,21 +56,21 @@ def compact(number):
     return number
 
 
-def calc_check_digit_legal(number):
+def calc_check_digit_legal(number: str) -> str:
     """Calculate the check digit for 8 digit legal entities. The number
     passed should not have the check digit included."""
     check = (11 - sum((8 - i) * int(n) for i, n in enumerate(number))) % 11
     return str((check or 1) % 10)
 
 
-def calc_check_digit_special(number):
+def calc_check_digit_special(number: str) -> str:
     """Calculate the check digit for special cases. The number passed
     should not have the first and last digits included."""
     check = sum((8 - i) * int(n) for i, n in enumerate(number)) % 11
     return str((8 - (10 - check) % 11) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -92,7 +94,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/cz/rc.py b/stdnum/cz/rc.py
index 79911c3..d4ddd64 100644
--- a/stdnum/cz/rc.py
+++ b/stdnum/cz/rc.py
@@ -47,19 +47,21 @@ InvalidLength: ...
 '710319/2745'
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' /').upper().strip()
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Split the date parts from the number and return the birth date."""
     number = compact(number)
     year = 1900 + int(number[0:2])
@@ -81,7 +83,7 @@ def get_birth_date(number):
         raise InvalidComponent()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid birth number. This checks the length,
     formatting, embedded date and check digit."""
     number = compact(number)
@@ -99,7 +101,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid birth number."""
     try:
         return bool(validate(number))
@@ -107,7 +109,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return number[:6] + '/' + number[6:]
diff --git a/stdnum/damm.py b/stdnum/damm.py
index e5fb256..abb983e 100644
--- a/stdnum/damm.py
+++ b/stdnum/damm.py
@@ -53,10 +53,18 @@ InvalidChecksum: ...
 9
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 
 
-_operation_table = (
+TYPE_CHECKING = False
+if TYPE_CHECKING:  # pragma: no cover (only used when type checking)
+    from collections.abc import Sequence
+    DammTable = Sequence[Sequence[int]]
+
+
+_operation_table: DammTable = (
     (0, 3, 1, 7, 5, 9, 8, 6, 4, 2),
     (7, 0, 9, 2, 1, 5, 4, 8, 6, 3),
     (4, 2, 0, 6, 8, 7, 1, 3, 5, 9),
@@ -69,7 +77,7 @@ _operation_table = (
     (2, 5, 8, 1, 4, 3, 6, 7, 9, 0))
 
 
-def checksum(number, table=None):
+def checksum(number: str, table: DammTable | None = None) -> int:
     """Calculate the Damm checksum over the provided number. The checksum is
     returned as an integer value and should be 0 when valid."""
     table = table or _operation_table
@@ -79,7 +87,7 @@ def checksum(number, table=None):
     return i
 
 
-def validate(number, table=None):
+def validate(number: str, table: DammTable | None = None) -> str:
     """Check if the number provided passes the Damm algorithm."""
     if not bool(number):
         raise InvalidFormat()
@@ -92,7 +100,7 @@ def validate(number, table=None):
     return number
 
 
-def is_valid(number, table=None):
+def is_valid(number: str, table: DammTable | None = None) -> bool:
     """Check if the number provided passes the Damm algorithm."""
     try:
         return bool(validate(number, table=table))
@@ -100,7 +108,7 @@ def is_valid(number, table=None):
         return False
 
 
-def calc_check_digit(number, table=None):
+def calc_check_digit(number: str, table: DammTable | None = None) -> str:
     """Calculate the extra digit that should be appended to the number to
     make it a valid number."""
     return str(checksum(number, table=table))
diff --git a/stdnum/de/__init__.py b/stdnum/de/__init__.py
index 1449961..df82ea3 100644
--- a/stdnum/de/__init__.py
+++ b/stdnum/de/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of German numbers."""
 
+from __future__ import annotations
+
 # provide businessid as an alias
 from stdnum.de import handelsregisternummer as businessid  # noqa: F401
diff --git a/stdnum/de/handelsregisternummer.py 
b/stdnum/de/handelsregisternummer.py
index 012e86f..9253d3c 100644
--- a/stdnum/de/handelsregisternummer.py
+++ b/stdnum/de/handelsregisternummer.py
@@ -50,6 +50,8 @@ Traceback (most recent call last):
 InvalidComponent: ...
 """
 
+from __future__ import annotations
+
 import re
 import unicodedata
 
@@ -57,6 +59,11 @@ from stdnum.exceptions import *
 from stdnum.util import clean
 
 
+TYPE_CHECKING = False
+if TYPE_CHECKING:  # pragma: no cover (typechecking only import)
+    from typing import Any
+
+
 # The known courts that have a Handelsregister
 GERMAN_COURTS = (
     'Aachen',
@@ -213,7 +220,7 @@ GERMAN_COURTS = (
 )
 
 
-def _to_min(court):
+def _to_min(court: str) -> str:
     """Convert the court name for quick comparison without encoding issues."""
     return ''.join(
         x for x in unicodedata.normalize('NFD', court.lower())
@@ -280,7 +287,7 @@ _formats = [
 ]
 
 
-def _split(number):
+def _split(number: str) -> tuple[str, str, str, str | None]:
     """Split the number into a court, registry, register number and
     optionally qualifier."""
     number = clean(number).strip()
@@ -291,17 +298,18 @@ def _split(number):
     raise InvalidFormat()
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     court, registry, number, qualifier = _split(number)
     return ' '.join(x for x in [court, registry, number, qualifier] if x)
 
 
-def validate(number, company_form=None):
+def validate(number: str, company_form: str | None = None) -> str:
     """Check if the number is a valid company registry number. If a
     company_form (eg. GmbH or PartG) is given, the number is validated to
     have the correct registry type."""
+    court: str | None
     court, registry, number, qualifier = _split(number)
     court = _courts.get(_to_min(court))
     if not court:
@@ -311,7 +319,7 @@ def validate(number, company_form=None):
     return ' '.join(x for x in [court, registry, number, qualifier] if x)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid company registry number."""
     try:
         return bool(validate(number))
@@ -323,7 +331,11 @@ def is_valid(number):
 _offeneregister_url = 'https://db.offeneregister.de/openregister.json'
 
 
-def check_offeneregister(number, timeout=30, verify=True):  # pragma: no cover 
(not part of normal test suite)
+def check_offeneregister(
+    number: str,
+    timeout: float = 30,
+    verify: bool | str = True,
+) -> dict[str, Any] | None:  # pragma: no cover (not part of normal test suite)
     """Retrieve registration information from the OffeneRegister.de web site.
 
     The `timeout` argument specifies the network timeout in seconds.
diff --git a/stdnum/de/idnr.py b/stdnum/de/idnr.py
index 5c5a179..b07f832 100644
--- a/stdnum/de/idnr.py
+++ b/stdnum/de/idnr.py
@@ -45,6 +45,8 @@ InvalidFormat: ...
 '36 574 261 809'
 """
 
+from __future__ import annotations
+
 from collections import defaultdict
 
 from stdnum.exceptions import *
@@ -52,13 +54,13 @@ from stdnum.iso7064 import mod_11_10
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -./,').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid tax identification number.
     This checks the length, formatting and check digit."""
     number = compact(number)
@@ -70,7 +72,7 @@ def validate(number):
         raise InvalidFormat()
     # In the first 10 digits exactly one digit must be repeated two or
     # three times and other digits can appear only once.
-    counter = defaultdict(int)
+    counter: dict[str, int] = defaultdict(int)
     for n in number[:10]:
         counter[n] += 1
     counts = [c for c in counter.values() if c > 1]
@@ -79,7 +81,7 @@ def validate(number):
     return mod_11_10.validate(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid tax identification number.
     This checks the length, formatting and check digit."""
     try:
@@ -88,7 +90,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join((number[:2], number[2:5], number[5:8], number[8:]))
diff --git a/stdnum/de/stnr.py b/stdnum/de/stnr.py
index 6c511f3..67ca9fe 100644
--- a/stdnum/de/stnr.py
+++ b/stdnum/de/stnr.py
@@ -50,14 +50,21 @@ Traceback (most recent call last):
 InvalidLength: ...
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
+TYPE_CHECKING = False
+if TYPE_CHECKING:  # pragma: no cover (typechecking only import)
+    from collections.abc import Iterable
+
+
 # The number formats per region (regional and country-wide format)
-_number_formats_per_region = {
+_number_formats_per_region_raw = {
     'Baden-Württemberg': ['FFBBBUUUUP', '28FF0BBBUUUUP'],
     'Bayern': ['FFFBBBUUUUP', '9FFF0BBBUUUUP'],
     'Berlin': ['FFBBBUUUUP', '11FF0BBBUUUUP'],
@@ -76,11 +83,11 @@ _number_formats_per_region = {
     'Thüringen': ['1FFBBBUUUUP', '41FF0BBBUUUUP'],
 }
 
-REGIONS = sorted(_number_formats_per_region.keys())
+REGIONS = sorted(_number_formats_per_region_raw.keys())
 """Valid regions recognised by this module."""
 
 
-def _clean_region(region):
+def _clean_region(region: str) -> str:
     """Convert the region name to something that we can use for comparison
     without running into encoding issues."""
     return ''.join(
@@ -90,28 +97,28 @@ def _clean_region(region):
 
 class _Format():
 
-    def __init__(self, fmt):
+    def __init__(self, fmt: str) -> None:
         self._fmt = fmt
         self._re = re.compile('^%s$' % re.sub(
             r'([FBUP])\1*',
             lambda x: r'(\d{%d})' % len(x.group(0)), fmt))
 
-    def match(self, number):
+    def match(self, number: str) -> re.Match[str] | None:
         return self._re.match(number)
 
-    def replace(self, f, b, u, p):
+    def replace(self, f: str, b: str, u: str, p: str) -> str:
         items = iter([f, b, u, p])
         return re.sub(r'([FBUP])\1*', lambda x: next(items), self._fmt)
 
 
 # Convert the structure to something that we can easily use
 _number_formats_per_region = dict(
-    (_clean_region(region), [
-        region, _Format(formats[0]), _Format(formats[1])])
-    for region, formats in _number_formats_per_region.items())
+    (_clean_region(region), (
+        region, _Format(formats[0]), _Format(formats[1])))
+    for region, formats in _number_formats_per_region_raw.items())
 
 
-def _get_formats(region=None):
+def _get_formats(region: str | None = None) -> Iterable[tuple[str, _Format, 
_Format]]:
     """Return the formats for the region."""
     if region:
         region = _clean_region(region)
@@ -121,13 +128,13 @@ def _get_formats(region=None):
     return _number_formats_per_region.values()
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -./,').strip()
 
 
-def validate(number, region=None):
+def validate(number: str, region: str | None = None) -> str:
     """Check if the number is a valid tax number. This checks the length and
     formatting. The region can be supplied to verify that the number is
     assigned in that region."""
@@ -142,7 +149,7 @@ def validate(number, region=None):
     return number
 
 
-def is_valid(number, region=None):
+def is_valid(number: str, region: str | None = None) -> bool:
     """Check if the number is a valid tax number. This checks the length and
     formatting. The region can be supplied to verify that the number is
     assigned in that region."""
@@ -152,7 +159,7 @@ def is_valid(number, region=None):
         return False
 
 
-def guess_regions(number):
+def guess_regions(number: str) -> list[str]:
     """Return a list of regions this number is valid for."""
     number = compact(number)
     return sorted(
@@ -160,7 +167,7 @@ def guess_regions(number):
         if region_fmt.match(number) or country_fmt.match(number))
 
 
-def to_regional_number(number):
+def to_regional_number(number: str) -> str:
     """Convert the number to a regional (10 or 11 digit) number."""
     number = compact(number)
     for _region, region_fmt, country_fmt in _get_formats():
@@ -170,16 +177,16 @@ def to_regional_number(number):
     raise InvalidFormat()
 
 
-def to_country_number(number, region=None):
+def to_country_number(number: str, region: str | None = None) -> str:
     """Convert the number to the nationally unique number. The region is
     needed if the number is not only valid for one particular region."""
     number = compact(number)
-    formats = (
+    formats_iter = (
         (region_fmt.match(number), country_fmt)
         for _region, region_fmt, country_fmt in _get_formats(region))
     formats = [
         (region_match, country_fmt)
-        for region_match, country_fmt in formats
+        for region_match, country_fmt in formats_iter
         if region_match]
     if not formats:
         raise InvalidFormat()
@@ -188,7 +195,7 @@ def to_country_number(number, region=None):
     return formats[0][1].replace(*formats[0][0].groups())
 
 
-def format(number, region=None):
+def format(number: str, region: str | None = None) -> str:
     """Reformat the passed number to the standard format."""
     number = compact(number)
     for _region, region_fmt, _country_fmt in _get_formats(region):
diff --git a/stdnum/de/vat.py b/stdnum/de/vat.py
index f2e9ca6..c09eadf 100644
--- a/stdnum/de/vat.py
+++ b/stdnum/de/vat.py
@@ -32,12 +32,14 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_11_10
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -./,').upper().strip()
@@ -46,7 +48,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid VAT number. This checks the
     length, formatting and check digit."""
     number = compact(number)
@@ -58,7 +60,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid VAT number. This checks the
     length, formatting and check digit."""
     try:
diff --git a/stdnum/de/wkn.py b/stdnum/de/wkn.py
index f2242bd..f303223 100644
--- a/stdnum/de/wkn.py
+++ b/stdnum/de/wkn.py
@@ -34,11 +34,13 @@ InvalidFormat: ...
 'DE000SKWM021'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip().upper()
@@ -48,7 +50,7 @@ def compact(number):
 _alphabet = '0123456789ABCDEFGH JKLMN PQRSTUVWXYZ'
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is valid. This checks the length and
     check digit."""
     number = compact(number)
@@ -59,7 +61,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is valid. This checks the length and
     check digit."""
     try:
@@ -68,7 +70,7 @@ def is_valid(number):
         return False
 
 
-def to_isin(number):
+def to_isin(number: str) -> str:
     """Convert the number to an ISIN."""
     from stdnum import isin
     return isin.from_natid('DE', number)
diff --git a/stdnum/dk/__init__.py b/stdnum/dk/__init__.py
index 4298d22..ef79e7a 100644
--- a/stdnum/dk/__init__.py
+++ b/stdnum/dk/__init__.py
@@ -20,6 +20,8 @@
 
 """Collection of Danish numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.dk import cpr as personalid  # noqa: F401
 from stdnum.dk import cvr as vat  # noqa: F401
diff --git a/stdnum/dk/cpr.py b/stdnum/dk/cpr.py
index 97eaf2b..901bc08 100644
--- a/stdnum/dk/cpr.py
+++ b/stdnum/dk/cpr.py
@@ -56,26 +56,28 @@ datetime.date(1962, 10, 21)
 '211062-5629'
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip()
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum. Note that the checksum isn't actually used
     any more. Valid numbers used to have a checksum of 0."""
     weights = (4, 3, 2, 7, 6, 5, 4, 3, 2, 1)
     return sum(w * int(n) for w, n in zip(weights, number)) % 11
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Split the date parts from the number and return the birth date."""
     number = compact(number)
     day = int(number[0:2])
@@ -93,7 +95,7 @@ def get_birth_date(number):
         raise InvalidComponent('The number does not contain valid birth date 
information.')
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid CPR number. This checks the
     length, formatting, embedded date and check digit."""
     number = compact(number)
@@ -107,7 +109,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid CPR number. This checks the
     length, formatting, embedded date and check digit."""
     try:
@@ -116,7 +118,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join((number[:6], number[6:]))
diff --git a/stdnum/dk/cvr.py b/stdnum/dk/cvr.py
index aa80402..849b8d9 100644
--- a/stdnum/dk/cvr.py
+++ b/stdnum/dk/cvr.py
@@ -30,11 +30,13 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -.,/:').upper().strip()
@@ -43,13 +45,13 @@ def compact(number):
     return number
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum."""
     weights = (2, 7, 6, 5, 4, 3, 2, 1)
     return sum(w * int(n) for w, n in zip(weights, number)) % 11
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid VAT number. This checks the
     length, formatting and check digit."""
     number = compact(number)
@@ -62,7 +64,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid VAT number. This checks the
     length, formatting and check digit."""
     try:
diff --git a/stdnum/do/__init__.py b/stdnum/do/__init__.py
index aa210e5..16c67f5 100644
--- a/stdnum/do/__init__.py
+++ b/stdnum/do/__init__.py
@@ -20,4 +20,6 @@
 
 """Collection of Dominican Republic numbers."""
 
+from __future__ import annotations
+
 from stdnum.do import rnc as vat  # noqa: F401
diff --git a/stdnum/do/cedula.py b/stdnum/do/cedula.py
index 5851b92..97a3a72 100644
--- a/stdnum/do/cedula.py
+++ b/stdnum/do/cedula.py
@@ -37,6 +37,8 @@ InvalidFormat: ...
 '224-0002211-1'
 """
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.do import rnc
 from stdnum.exceptions import *
@@ -145,13 +147,13 @@ whitelist = set('''
 '''.split())
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid cedula."""
     number = compact(number)
     if not isdigits(number):
@@ -163,7 +165,7 @@ def validate(number):
     return luhn.validate(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid cedula."""
     try:
         return bool(validate(number))
@@ -171,13 +173,17 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join((number[:3], number[3:-1], number[-1]))
 
 
-def check_dgii(number, timeout=30, verify=True):  # pragma: no cover
+def check_dgii(
+    number: str,
+    timeout: float = 30,
+    verify: bool | str = True,
+) -> dict[str, str] | None:  # pragma: no cover
     """Lookup the number using the DGII online web service.
 
     This uses the validation service run by the the Dirección General de
diff --git a/stdnum/do/ncf.py b/stdnum/do/ncf.py
index b17760e..c67a3b8 100644
--- a/stdnum/do/ncf.py
+++ b/stdnum/do/ncf.py
@@ -56,11 +56,13 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip().upper()
@@ -95,7 +97,7 @@ _ecf_document_types = (
 )
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid NCF."""
     number = compact(number)
     if len(number) == 13:
@@ -118,7 +120,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid NCF."""
     try:
         return bool(validate(number))
@@ -126,7 +128,7 @@ def is_valid(number):
         return False
 
 
-def _convert_result(result):  # pragma: no cover
+def _convert_result(result: dict[str, str]) -> dict[str, str]:  # pragma: no 
cover
     """Translate SOAP result entries into dictionaries."""
     translation = {
         'NOMBRE': 'name',
@@ -157,7 +159,14 @@ def _convert_result(result):  # pragma: no cover
         for key, value in result.items())
 
 
-def check_dgii(rnc, ncf, buyer_rnc=None, security_code=None, timeout=30, 
verify=True):  # pragma: no cover
+def check_dgii(
+    rnc: str,
+    ncf: str,
+    buyer_rnc: str | None = None,
+    security_code: str | None = None,
+    timeout: float = 30,
+    verify: bool | str = True,
+) -> dict[str, str] | None:  # pragma: no cover
     """Validate the RNC, NCF combination on using the DGII online web service.
 
     This uses the validation service run by the the Dirección General de
@@ -198,7 +207,7 @@ def check_dgii(rnc, ncf, buyer_rnc=None, 
security_code=None, timeout=30, verify=
         }
 
     Will return None if the number is invalid or unknown."""
-    import lxml.html
+    import lxml.html  # type: ignore
     import requests
     from stdnum.do.rnc import compact as rnc_compact  # noqa: I003
     rnc = rnc_compact(rnc)
diff --git a/stdnum/do/rnc.py b/stdnum/do/rnc.py
index 10c5b43..63570d3 100644
--- a/stdnum/do/rnc.py
+++ b/stdnum/do/rnc.py
@@ -39,6 +39,8 @@ InvalidChecksum: ...
 '1-31-24679-6'
 """
 
+from __future__ import annotations
+
 import json
 
 from stdnum.exceptions import *
@@ -58,20 +60,20 @@ dgii_wsdl = 
'https://www.dgii.gov.do/wsMovilDGII/WSMovilDGII.asmx?WSDL'
 """The WSDL URL of DGII validation service."""
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     weights = (7, 9, 8, 6, 5, 4, 3, 2)
     check = sum(w * int(n) for w, n in zip(weights, number)) % 11
     return str((10 - check) % 9 + 1)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid RNC."""
     number = compact(number)
     if not isdigits(number):
@@ -85,7 +87,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid RNC."""
     try:
         return bool(validate(number))
@@ -93,13 +95,13 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join((number[:1], number[1:3], number[3:-1], number[-1]))
 
 
-def _convert_result(result):  # pragma: no cover
+def _convert_result(result: str) -> dict[str, str]:  # pragma: no cover
     """Translate SOAP result entries into dicts."""
     translation = {
         'RGE_RUC': 'rnc',
@@ -115,7 +117,11 @@ def _convert_result(result):  # pragma: no cover
         for key, value in json.loads(result.replace('\n', '\\n').replace('\t', 
'\\t')).items())
 
 
-def check_dgii(number, timeout=30, verify=True):  # pragma: no cover
+def check_dgii(
+    number: str,
+    timeout: float = 30,
+    verify: bool | str = True,
+) -> dict[str, str] | None:  # pragma: no cover
     """Lookup the number using the DGII online web service.
 
     This uses the validation service run by the the Dirección General de
@@ -158,7 +164,13 @@ def check_dgii(number, timeout=30, verify=True):  # 
pragma: no cover
     return _convert_result(result[0])
 
 
-def search_dgii(keyword, end_at=10, start_at=1, timeout=30, verify=True):  # 
pragma: no cover
+def search_dgii(
+    keyword: str,
+    end_at: int = 10,
+    start_at: int = 1,
+    timeout: float = 30,
+    verify: bool | str = True,
+) -> list[dict[str, str]]:  # pragma: no cover
     """Search the DGII online web service using the keyword.
 
     This uses the validation service run by the the Dirección General de
diff --git a/stdnum/dz/__init__.py b/stdnum/dz/__init__.py
index c0e8e9f..812c3ec 100644
--- a/stdnum/dz/__init__.py
+++ b/stdnum/dz/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Algerian numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.dz import nif as vat  # noqa: F401
diff --git a/stdnum/dz/nif.py b/stdnum/dz/nif.py
index 48a16fd..3f4857e 100644
--- a/stdnum/dz/nif.py
+++ b/stdnum/dz/nif.py
@@ -60,11 +60,13 @@ InvalidFormat: ...
 '00021600180833713010'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators, removes surrounding
@@ -73,7 +75,7 @@ def compact(number):
     return clean(number, ' ')
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Algeria NIF number.
 
     This checks the length and formatting.
@@ -86,7 +88,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Algeria NIF number."""
     try:
         return bool(validate(number))
@@ -94,6 +96,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/ean.py b/stdnum/ean.py
index f7e4b63..f84503c 100644
--- a/stdnum/ean.py
+++ b/stdnum/ean.py
@@ -30,24 +30,26 @@ module handles numbers EAN-13, EAN-8, UPC (12-digit) and 
GTIN (EAN-14) format.
 '98412345678908'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the EAN to the minimal representation. This strips the number
     of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the EAN check digit for 13-digit numbers. The number passed
     should not have the check bit included."""
     return str((10 - sum((3, 1)[i % 2] * int(n)
                          for i, n in enumerate(reversed(number)))) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid EAN-13. This checks the length
     and the check bit but does not check whether a known GS1 Prefix and
     company identifier are referenced."""
@@ -61,7 +63,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid EAN-13. This checks the length
     and the check bit but does not check whether a known GS1 Prefix and
     company identifier are referenced."""
diff --git a/stdnum/ec/__init__.py b/stdnum/ec/__init__.py
index 6e91906..3c35346 100644
--- a/stdnum/ec/__init__.py
+++ b/stdnum/ec/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Ecuadorian numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.ec import ruc as vat  # noqa: F401
diff --git a/stdnum/ec/ci.py b/stdnum/ec/ci.py
index c97c193..0046266 100644
--- a/stdnum/ec/ci.py
+++ b/stdnum/ec/ci.py
@@ -35,24 +35,27 @@ Traceback (most recent call last):
 InvalidLength: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').upper().strip()
 
 
-def _checksum(number):
+def _checksum(number: str) -> int:
     """Calculate a checksum over the number."""
-    fold = lambda x: x - 9 if x > 9 else x
+    def fold(x: int) -> int:
+        return x - 9 if x > 9 else x
     return sum(fold((2, 1)[i % 2] * int(n))
                for i, n in enumerate(number)) % 10
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid CI number. This checks the
     length, formatting and check digit."""
     number = compact(number)
@@ -69,7 +72,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid CI number. This checks the
     length, formatting and check digit."""
     try:
diff --git a/stdnum/ec/ruc.py b/stdnum/ec/ruc.py
index c49b4fc..d2b45ca 100644
--- a/stdnum/ec/ruc.py
+++ b/stdnum/ec/ruc.py
@@ -36,6 +36,8 @@ Traceback (most recent call last):
 InvalidLength: ...
 """
 
+from __future__ import annotations
+
 from stdnum.ec import ci
 from stdnum.exceptions import *
 from stdnum.util import isdigits
@@ -48,12 +50,12 @@ __all__ = ['compact', 'validate', 'is_valid']
 compact = ci.compact
 
 
-def _checksum(number, weights):
+def _checksum(number: str, weights: list[int]) -> int:
     """Calculate a checksum over the number given the weights."""
     return sum(w * int(n) for w, n in zip(weights, number)) % 11
 
 
-def _validate_natural(number):
+def _validate_natural(number: str) -> str:
     """Check if the number is a valid natural RUC (CI plus establishment)."""
     if number[-3:] == '000':
         raise InvalidComponent()  # establishment number wrong
@@ -61,25 +63,25 @@ def _validate_natural(number):
     return number
 
 
-def _validate_public(number):
+def _validate_public(number: str) -> str:
     """Check if the number is a valid public RUC."""
     if number[-4:] == '0000':
         raise InvalidComponent()  # establishment number wrong
-    if _checksum(number[:9], (3, 2, 7, 6, 5, 4, 3, 2, 1)) != 0:
+    if _checksum(number[:9], [3, 2, 7, 6, 5, 4, 3, 2, 1]) != 0:
         raise InvalidChecksum()
     return number
 
 
-def _validate_juridical(number):
+def _validate_juridical(number: str) -> str:
     """Check if the number is a valid juridical RUC."""
     if number[-3:] == '000':
         raise InvalidComponent()  # establishment number wrong
-    if _checksum(number[:10], (4, 3, 2, 7, 6, 5, 4, 3, 2, 1)) != 0:
+    if _checksum(number[:10], [4, 3, 2, 7, 6, 5, 4, 3, 2, 1]) != 0:
         raise InvalidChecksum()
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid RUC number. This checks the
     length, formatting, check digit and check sum."""
     number = compact(number)
@@ -109,7 +111,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid RUC number. This checks the
     length, formatting and check digit."""
     try:
diff --git a/stdnum/ee/__init__.py b/stdnum/ee/__init__.py
index 7728e2d..9662449 100644
--- a/stdnum/ee/__init__.py
+++ b/stdnum/ee/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Estonian numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.ee import kmkr as vat  # noqa: F401
diff --git a/stdnum/ee/ik.py b/stdnum/ee/ik.py
index be68fc6..b8465bb 100644
--- a/stdnum/ee/ik.py
+++ b/stdnum/ee/ik.py
@@ -39,19 +39,21 @@ InvalidChecksum: ...
 datetime.date(1968, 5, 28)
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip()
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Split the date parts from the number and return the birth date."""
     number = compact(number)
     if number[0] in '12':
@@ -73,7 +75,7 @@ def get_birth_date(number):
         raise InvalidComponent()
 
 
-def get_gender(number):
+def get_gender(number: str) -> str:
     """Get the person's birth gender ('M' or 'F')."""
     number = compact(number)
     if number[0] in '1357':
@@ -84,7 +86,7 @@ def get_gender(number):
         raise InvalidComponent()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     check = sum(((i % 9) + 1) * int(n)
                 for i, n in enumerate(number[:-1])) % 11
@@ -94,7 +96,7 @@ def calc_check_digit(number):
     return str(check % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is valid. This checks the length,
     formatting, embedded date and check digit."""
     number = compact(number)
@@ -108,7 +110,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is valid. This checks the length,
     formatting, embedded date and check digit."""
     try:
diff --git a/stdnum/ee/kmkr.py b/stdnum/ee/kmkr.py
index 24ddbb3..e94ad8b 100644
--- a/stdnum/ee/kmkr.py
+++ b/stdnum/ee/kmkr.py
@@ -30,11 +30,13 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' ').upper().strip()
@@ -43,13 +45,13 @@ def compact(number):
     return number
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum."""
     weights = (3, 7, 1, 3, 7, 1, 3, 7, 1)
     return sum(w * int(n) for w, n in zip(weights, number)) % 10
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid VAT number. This checks the
     length, formatting and check digit."""
     number = compact(number)
@@ -62,7 +64,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid VAT number. This checks the
     length, formatting and check digit."""
     try:
diff --git a/stdnum/ee/registrikood.py b/stdnum/ee/registrikood.py
index 8f0ebc1..ab4427c 100644
--- a/stdnum/ee/registrikood.py
+++ b/stdnum/ee/registrikood.py
@@ -47,18 +47,20 @@ Traceback (most recent call last):
 InvalidComponent: ...
 """
 
+from __future__ import annotations
+
 from stdnum.ee.ik import calc_check_digit
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is valid. This checks the length and
     check digit."""
     number = compact(number)
@@ -73,7 +75,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is valid. This checks the length and
     check digit."""
     try:
diff --git a/stdnum/eg/__init__.py b/stdnum/eg/__init__.py
index dfa5d08..1026aca 100644
--- a/stdnum/eg/__init__.py
+++ b/stdnum/eg/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Egypt numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.eg import tn as vat  # noqa: F401
diff --git a/stdnum/eg/tn.py b/stdnum/eg/tn.py
index aef9ca5..07559db 100644
--- a/stdnum/eg/tn.py
+++ b/stdnum/eg/tn.py
@@ -44,6 +44,8 @@ InvalidFormat: ...
 '100-531-385'
 """  # noqa: E501
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
@@ -74,7 +76,7 @@ _ARABIC_NUMBERS_MAP = {
 }
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -83,7 +85,7 @@ def compact(number):
     return ''.join((_ARABIC_NUMBERS_MAP.get(c, c) for c in clean(number, ' 
-/').strip()))
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Egypt Tax Number number.
 
     This checks the length and formatting.
@@ -96,7 +98,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Egypt Tax Number number."""
     try:
         return bool(validate(number))
@@ -104,7 +106,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join([number[:3], number[3:-3], number[-3:]])
diff --git a/stdnum/es/__init__.py b/stdnum/es/__init__.py
index e74786c..cc88137 100644
--- a/stdnum/es/__init__.py
+++ b/stdnum/es/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Spanish numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.es import nif as vat  # noqa: F401
diff --git a/stdnum/es/cae.py b/stdnum/es/cae.py
index 19b9717..b203881 100644
--- a/stdnum/es/cae.py
+++ b/stdnum/es/cae.py
@@ -50,6 +50,8 @@ False
 'ES00008V1488Q'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
@@ -203,13 +205,13 @@ _ACTIVITY_KEYS = {
 }
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number).upper().strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid CAE number. This checks the
     length and formatting."""
     number = compact(number)
@@ -230,7 +232,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid CAE number. This checks the
     length and formatting."""
     try:
diff --git a/stdnum/es/ccc.py b/stdnum/es/ccc.py
index 3306c20..ce0a8b1 100644
--- a/stdnum/es/ccc.py
+++ b/stdnum/es/ccc.py
@@ -62,17 +62,19 @@ InvalidChecksum: ...
 'ES2121000418450200051331'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip().upper()
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join([
@@ -84,13 +86,13 @@ def format(number):
     ])
 
 
-def _calc_check_digit(number):
+def _calc_check_digit(number: str) -> str:
     """Calculate a single check digit on the provided part of the number."""
     check = sum(int(n) * 2 ** i for i, n in enumerate(number)) % 11
     return str(check if check < 2 else 11 - check)
 
 
-def calc_check_digits(number):
+def calc_check_digits(number: str) -> str:
     """Calculate the check digits for the number. The supplied number should
     have check digits included but are ignored."""
     number = compact(number)
@@ -98,7 +100,7 @@ def calc_check_digits(number):
         _calc_check_digit('00' + number[:8]) + _calc_check_digit(number[10:]))
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid CCC."""
     number = compact(number)
     if len(number) != 20:
@@ -110,7 +112,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid CCC."""
     try:
         return bool(validate(number))
@@ -118,7 +120,7 @@ def is_valid(number):
         return False
 
 
-def to_iban(number):
+def to_iban(number: str) -> str:
     """Convert the number to an IBAN."""
     from stdnum import iban
     separator = ' ' if ' ' in number else ''
diff --git a/stdnum/es/cif.py b/stdnum/es/cif.py
index 395db81..ea9dd0d 100644
--- a/stdnum/es/cif.py
+++ b/stdnum/es/cif.py
@@ -50,6 +50,8 @@ InvalidFormat: ...
 ('A', '13', '58562', '5')
 """
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.es import dni
 from stdnum.exceptions import *
@@ -63,7 +65,7 @@ __all__ = ['compact', 'validate', 'is_valid', 'split']
 compact = dni.compact
 
 
-def calc_check_digits(number):
+def calc_check_digits(number: str) -> str:
     """Calculate the check digits for the specified number. The number
     passed should not have the check digit included. This function returns
     both the number and character check digit candidates."""
@@ -71,7 +73,7 @@ def calc_check_digits(number):
     return check + 'JABCDEFGHI'[int(check)]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid DNI number. This checks the
     length, formatting and check digit."""
     number = compact(number)
@@ -91,7 +93,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid DNI number. This checks the
     length, formatting and check digit."""
     try:
@@ -100,7 +102,7 @@ def is_valid(number):
         return False
 
 
-def split(number):
+def split(number: str) -> tuple[str, str, str, str]:
     """Split the provided number into a letter to define the type of
     organisation, two digits that specify a province, a 5 digit sequence
     number within the province and a check digit."""
diff --git a/stdnum/es/cups.py b/stdnum/es/cups.py
index 88ec9d9..f666eac 100644
--- a/stdnum/es/cups.py
+++ b/stdnum/es/cups.py
@@ -53,17 +53,19 @@ InvalidChecksum: ...
 'ES 1234 1234 5678 9012 JY 1F'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip().upper()
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join((
@@ -77,14 +79,14 @@ def format(number):
     )).strip()
 
 
-def calc_check_digits(number):
+def calc_check_digits(number: str) -> str:
     """Calculate the check digits for the number."""
     alphabet = 'TRWAGMYFPDXBNJZSQVHLCKE'
     check0, check1 = divmod(int(number[2:18]) % 529, 23)
     return alphabet[check0] + alphabet[check1]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid CUPS. This checks length,
     formatting and check digits."""
     number = compact(number)
@@ -105,7 +107,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid CUPS."""
     try:
         return bool(validate(number))
diff --git a/stdnum/es/dni.py b/stdnum/es/dni.py
index 379ac2f..f50ebc8 100644
--- a/stdnum/es/dni.py
+++ b/stdnum/es/dni.py
@@ -38,23 +38,25 @@ Traceback (most recent call last):
 InvalidLength: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').upper().strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     return 'TRWAGMYFPDXBNJZSQVHLCKE'[int(number) % 23]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid DNI number. This checks the
     length, formatting and check digit."""
     number = compact(number)
@@ -67,7 +69,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid DNI number. This checks the
     length, formatting and check digit."""
     try:
diff --git a/stdnum/es/iban.py b/stdnum/es/iban.py
index 9c31296..7de1403 100644
--- a/stdnum/es/iban.py
+++ b/stdnum/es/iban.py
@@ -44,6 +44,8 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum import iban
 from stdnum.es import ccc
 from stdnum.exceptions import *
@@ -56,7 +58,7 @@ compact = iban.compact
 format = iban.format
 
 
-def to_ccc(number):
+def to_ccc(number: str) -> str:
     """Return the CCC (Código Cuenta Corriente) part of the number."""
     number = compact(number)
     if not number.startswith('ES'):
@@ -64,14 +66,14 @@ def to_ccc(number):
     return number[4:]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid Spanish IBAN."""
     number = iban.validate(number, check_country=False)
     ccc.validate(to_ccc(number))
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid Spanish IBAN."""
     try:
         return bool(validate(number))
diff --git a/stdnum/es/nie.py b/stdnum/es/nie.py
index 0c0a73b..52462a2 100644
--- a/stdnum/es/nie.py
+++ b/stdnum/es/nie.py
@@ -40,6 +40,8 @@ Traceback (most recent call last):
 InvalidLength: ...
 """
 
+from __future__ import annotations
+
 from stdnum.es import dni
 from stdnum.exceptions import *
 from stdnum.util import isdigits
@@ -52,7 +54,7 @@ __all__ = ['compact', 'calc_check_digit', 'validate', 
'is_valid']
 compact = dni.compact
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     # replace XYZ with 012
@@ -60,7 +62,7 @@ def calc_check_digit(number):
     return dni.calc_check_digit(number)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid NIE. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -73,7 +75,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid NIE. This checks the length,
     formatting and check digit."""
     try:
diff --git a/stdnum/es/nif.py b/stdnum/es/nif.py
index 76f0570..cb05bd6 100644
--- a/stdnum/es/nif.py
+++ b/stdnum/es/nif.py
@@ -44,12 +44,14 @@ InvalidChecksum: ...
 'M1234567L'
 """
 
+from __future__ import annotations
+
 from stdnum.es import cif, dni, nie
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').upper().strip()
@@ -58,7 +60,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid VAT number. This checks the
     length, formatting and check digit."""
     number = compact(number)
@@ -85,7 +87,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid VAT number. This checks the
     length, formatting and check digit."""
     try:
diff --git a/stdnum/es/postal_code.py b/stdnum/es/postal_code.py
index 0087fd1..81ea4c2 100644
--- a/stdnum/es/postal_code.py
+++ b/stdnum/es/postal_code.py
@@ -54,17 +54,18 @@ Traceback (most recent call last):
 InvalidLength: ...
 """
 
+from __future__ import annotations
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation."""
     return clean(number, ' ').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid postal code."""
     number = compact(number)
     if len(number) != 5:
@@ -76,7 +77,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid postal code."""
     try:
         return bool(validate(number))
diff --git a/stdnum/es/referenciacatastral.py b/stdnum/es/referenciacatastral.py
index 3e7eae3..19123c9 100644
--- a/stdnum/es/referenciacatastral.py
+++ b/stdnum/es/referenciacatastral.py
@@ -54,6 +54,8 @@ InvalidChecksum: ...
 '4A08169 P03PRAT 0001 LR'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean
 
@@ -61,13 +63,13 @@ from stdnum.util import clean
 alphabet = 'ABCDEFGHIJKLMNÑOPQRSTUVWXYZ0123456789'
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip().upper()
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join([
@@ -81,7 +83,7 @@ def format(number):
 # implementation by Vicente Sancho that can be found at
 # https://trellat.es/validar-la-referencia-catastral-en-javascript/
 
-def _check_digit(number):
+def _check_digit(number: str) -> str:
     """Calculate a single check digit on the provided part of the number."""
     weights = (13, 15, 12, 5, 4, 17, 9, 21, 3, 7, 1)
     s = sum(w * (int(n) if n.isdigit() else alphabet.find(n) + 1)
@@ -89,7 +91,7 @@ def _check_digit(number):
     return 'MQWERTYUIOPASDFGHJKLBZX'[s % 23]
 
 
-def calc_check_digits(number):
+def calc_check_digits(number: str) -> str:
     """Calculate the check digits for the number."""
     number = compact(number)
     return (
@@ -97,7 +99,7 @@ def calc_check_digits(number):
         _check_digit(number[7:14] + number[14:18]))
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Cadastral Reference. This checks the
     length, formatting and check digits."""
     number = compact(number)
@@ -110,7 +112,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Cadastral Reference."""
     try:
         return bool(validate(number))
diff --git a/stdnum/eu/at_02.py b/stdnum/eu/at_02.py
index acafa90..fa750ac 100644
--- a/stdnum/eu/at_02.py
+++ b/stdnum/eu/at_02.py
@@ -38,6 +38,8 @@ contains the country-specific identifier.
 '23'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_97_10
 from stdnum.util import clean
@@ -47,20 +49,20 @@ from stdnum.util import clean
 _alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the AT-02 number to the minimal representation. This strips
     the number of any valid separators and removes invalid characters."""
     return clean(number, ' -/?:().m\'+"').strip().upper()
 
 
-def _to_base10(number):
+def _to_base10(number: str) -> str:
     """Prepare the number to its base10 representation so it can be checked
     with the ISO 7064 Mod 97, 10 algorithm. That means excluding positions 5
     to 7 and moving the first four digits to the end."""
     return ''.join(str(_alphabet.index(x)) for x in number[7:] + number[:4])
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid AT-02."""
     number = compact(number)
     try:
@@ -72,7 +74,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid AT-02."""
     try:
         return bool(validate(number))
@@ -80,7 +82,7 @@ def is_valid(number):
         return False
 
 
-def calc_check_digits(number):
+def calc_check_digits(number: str) -> str:
     """Calculate the check digits that should be put in the number to make it
     valid. Check digits in the supplied number are ignored."""
     number = compact(number)
diff --git a/stdnum/eu/banknote.py b/stdnum/eu/banknote.py
index 24860e0..160ca13 100644
--- a/stdnum/eu/banknote.py
+++ b/stdnum/eu/banknote.py
@@ -34,23 +34,25 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').upper().strip()
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum over the number."""
     # replace letters by their ASCII number
     return sum(int(x) if isdigits(x) else ord(x) for x in number) % 9
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid banknote serial number."""
     number = compact(number)
     if not number[:2].isalnum() or not isdigits(number[2:]):
@@ -64,7 +66,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid banknote serial number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/eu/ecnumber.py b/stdnum/eu/ecnumber.py
index a405496..138a936 100644
--- a/stdnum/eu/ecnumber.py
+++ b/stdnum/eu/ecnumber.py
@@ -38,6 +38,8 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -47,7 +49,7 @@ from stdnum.util import clean
 _ec_number_re = re.compile(r'^[0-9]{3}-[0-9]{3}-[0-9]$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation."""
     number = clean(number, ' ').strip()
     if '-' not in number:
@@ -55,7 +57,7 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for the number. The passed number should not
     have the check digit included."""
     number = compact(number).replace('-', '')
@@ -63,7 +65,7 @@ def calc_check_digit(number):
         sum((i + 1) * int(n) for i, n in enumerate(number)) % 11)[0]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid EC Number."""
     number = compact(number)
     if not len(number) == 9:
@@ -75,7 +77,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid EC Number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/eu/eic.py b/stdnum/eu/eic.py
index 6342518..cf3c96b 100644
--- a/stdnum/eu/eic.py
+++ b/stdnum/eu/eic.py
@@ -44,6 +44,8 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean
 
@@ -51,20 +53,20 @@ from stdnum.util import clean
 _alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-'
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding white space."""
     return clean(number, ' ').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for the number."""
     number = compact(number)
     s = sum((16 - i) * _alphabet.index(n) for i, n in enumerate(number[:15]))
     return _alphabet[36 - ((s - 1) % 37)]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is valid. This checks the length, format and check
     digit."""
     number = compact(number)
@@ -79,7 +81,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is valid. This checks the length, format and check
     digit."""
     try:
diff --git a/stdnum/eu/nace.py b/stdnum/eu/nace.py
index d667f4f..df3e2c0 100644
--- a/stdnum/eu/nace.py
+++ b/stdnum/eu/nace.py
@@ -52,19 +52,21 @@ InvalidLength: ...
 '62.01'
 """  # noqa: E501
 
+from __future__ import annotations
+
 import warnings
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, '.').strip()
 
 
-def info(number):
+def info(number: str) -> dict[str, str]:
     """Lookup information about the specified NACE. This returns a dict."""
     number = compact(number)
     from stdnum import numdb
@@ -76,12 +78,12 @@ def info(number):
     return info
 
 
-def get_label(number):
+def get_label(number: str) -> str:
     """Lookup the category label for the number."""
     return info(number)['label']
 
 
-def label(number):  # pragma: no cover (deprecated function)
+def label(number: str) -> str:  # pragma: no cover (deprecated function)
     """DEPRECATED: use `get_label()` instead."""  # noqa: D40
     warnings.warn(
         'label() has been to get_label()',
@@ -89,7 +91,7 @@ def label(number):  # pragma: no cover (deprecated function)
     return get_label(number)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid NACE. This checks the format and
     searches the registry to see if it exists."""
     number = compact(number)
@@ -105,7 +107,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid NACE. This checks the format and
     searches the registry to see if it exists."""
     try:
@@ -114,6 +116,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return '.'.join((number[:2], number[2:])).strip('.')
diff --git a/stdnum/eu/oss.py b/stdnum/eu/oss.py
index e9d0349..7bb3f65 100644
--- a/stdnum/eu/oss.py
+++ b/stdnum/eu/oss.py
@@ -50,6 +50,7 @@ More information:
 'EU372022452'
 """
 
+from __future__ import annotations
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
@@ -88,12 +89,12 @@ ISO_3166_1_MEMBER_STATES = (
 """The collection of member state codes (for MSI) that may make up a VAT 
number."""
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Compact European VAT Number"""
     return clean(number, ' -').upper().strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Validate European VAT Number"""
     number = compact(number)
     if number.startswith('EU'):
@@ -111,7 +112,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number. This performs the
     country-specific check for the number."""
     try:
diff --git a/stdnum/eu/vat.py b/stdnum/eu/vat.py
index c1b6d00..062abaa 100644
--- a/stdnum/eu/vat.py
+++ b/stdnum/eu/vat.py
@@ -39,9 +39,14 @@ that country.
 ['nl']
 """
 
+from __future__ import annotations
+
+import datetime
+
 from stdnum.eu import oss
 from stdnum.exceptions import *
-from stdnum.util import clean, get_cc_module, get_soap_client
+from stdnum.util import (
+    NumberValidationModule, clean, get_cc_module, get_soap_client)
 
 
 MEMBER_STATES = set([
@@ -59,7 +64,7 @@ vies_wsdl = 
'https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl'
 """The WSDL URL of the VAT Information Exchange System (VIES)."""
 
 
-def _get_cc_module(cc):
+def _get_cc_module(cc: str) -> NumberValidationModule | None:
     """Get the VAT number module based on the country code."""
     # Greece uses a "wrong" country code
     cc = cc.lower()
@@ -76,7 +81,7 @@ def _get_cc_module(cc):
     return _country_modules[cc]
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, '').upper().strip()
@@ -90,7 +95,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This performs the
     country-specific check for the number."""
     number = clean(number, '').upper().strip()
@@ -104,7 +109,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number. This performs the
     country-specific check for the number."""
     try:
@@ -113,17 +118,21 @@ def is_valid(number):
         return False
 
 
-def guess_country(number):
+def guess_country(number: str) -> list[str]:
     """Guess the country code based on the number. This checks the number
     against each of the validation routines and returns the list of countries
     for which it is valid. This returns lower case codes and returns gr (not
     el) for Greece."""
     return [cc
             for cc in MEMBER_STATES
-            if _get_cc_module(cc).is_valid(number)]
+            if _get_cc_module(cc).is_valid(number)]  # type: ignore[union-attr]
 
 
-def check_vies(number, timeout=30, verify=True):  # pragma: no cover (not part 
of normal test suite)
+def check_vies(
+    number: str,
+    timeout: float = 30,
+    verify: bool | str = True,
+) -> dict[str, str | bool | datetime.date]:  # pragma: no cover (not part of 
normal test suite)
     """Use the EU VIES service to validate the provided number.
 
     Query the online European Commission VAT Information Exchange System
@@ -142,10 +151,15 @@ def check_vies(number, timeout=30, verify=True):  # 
pragma: no cover (not part o
     # network access for the tests and unnecessarily load the VIES website
     number = compact(number)
     client = get_soap_client(vies_wsdl, timeout=timeout, verify=verify)
-    return client.checkVat(number[:2], number[2:])
+    return client.checkVat(number[:2], number[2:])  # type: 
ignore[no-any-return]
 
 
-def check_vies_approx(number, requester, timeout=30, verify=True):  # pragma: 
no cover
+def check_vies_approx(
+    number: str,
+    requester: str,
+    timeout: float = 30,
+    verify: bool | str = True,
+) -> dict[str, str | bool | datetime.date]:  # pragma: no cover
     """Use the EU VIES service to validate the provided number.
 
     Query the online European Commission VAT Information Exchange System
@@ -167,6 +181,6 @@ def check_vies_approx(number, requester, timeout=30, 
verify=True):  # pragma: no
     number = compact(number)
     requester = compact(requester)
     client = get_soap_client(vies_wsdl, timeout=timeout, verify=verify)
-    return client.checkVatApprox(
+    return client.checkVatApprox(  # type: ignore[no-any-return]
         countryCode=number[:2], vatNumber=number[2:],
         requesterCountryCode=requester[:2], requesterVatNumber=requester[2:])
diff --git a/stdnum/exceptions.py b/stdnum/exceptions.py
index 7a8aacb..daa9d2f 100644
--- a/stdnum/exceptions.py
+++ b/stdnum/exceptions.py
@@ -24,6 +24,8 @@ The validation functions of stdnum should raise one of the 
below exceptions
 when validation of the number fails.
 """
 
+from __future__ import annotations
+
 
 __all__ = ['ValidationError', 'InvalidFormat', 'InvalidChecksum',
            'InvalidLength', 'InvalidComponent']
@@ -35,7 +37,7 @@ class ValidationError(ValueError):
     This exception should normally not be raised, only subclasses of this
     exception."""
 
-    def __str__(self):
+    def __str__(self) -> str:
         """Return the exception message."""
         return ''.join(self.args[:1]) or getattr(self, 'message', '')
 
diff --git a/stdnum/fi/__init__.py b/stdnum/fi/__init__.py
index 80129fc..c47cc8d 100644
--- a/stdnum/fi/__init__.py
+++ b/stdnum/fi/__init__.py
@@ -20,6 +20,8 @@
 
 """Collection of Finnish numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.fi import alv as vat  # noqa: F401
 from stdnum.fi import hetu as personalid  # noqa: F401
diff --git a/stdnum/fi/alv.py b/stdnum/fi/alv.py
index ca90bad..3c38a6b 100644
--- a/stdnum/fi/alv.py
+++ b/stdnum/fi/alv.py
@@ -30,11 +30,13 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').upper().strip()
@@ -43,13 +45,13 @@ def compact(number):
     return number
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum."""
     weights = (7, 9, 10, 5, 8, 4, 2, 1)
     return sum(w * int(n) for w, n in zip(weights, number)) % 11
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -62,7 +64,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/fi/associationid.py b/stdnum/fi/associationid.py
index 4374671..4535d1c 100644
--- a/stdnum/fi/associationid.py
+++ b/stdnum/fi/associationid.py
@@ -42,6 +42,8 @@ InvalidFormat: The number has an invalid format.
 '1.234'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
@@ -53,13 +55,13 @@ _lownumbers = set((
     83, 84, 85, 89, 92))
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -._+').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Finnish association register number.
     This checks the length and format."""
     number = compact(number)
@@ -72,7 +74,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid association register number."""
     try:
         return bool(validate(number))
@@ -80,7 +82,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     if len(number) <= 3:
diff --git a/stdnum/fi/hetu.py b/stdnum/fi/hetu.py
index b2b866f..4ad5798 100644
--- a/stdnum/fi/hetu.py
+++ b/stdnum/fi/hetu.py
@@ -41,6 +41,8 @@ InvalidComponent: ...
 '131052A308T'
 """
 
+from __future__ import annotations
+
 import datetime
 import re
 
@@ -62,17 +64,17 @@ _hetu_re = 
re.compile(r'^(?P<day>[0123]\d)(?P<month>[01]\d)(?P<year>\d\d)'
                       r'(?P<control>[0-9ABCDEFHJKLMNPRSTUVWXY])$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the HETU to the minimal representation. This strips
     surrounding whitespace and converts it to upper case."""
     return clean(number, '').upper().strip()
 
 
-def _calc_checksum(number):
+def _calc_checksum(number: str) -> str:
     return '0123456789ABCDEFHJKLMNPRSTUVWXY'[int(number) % 31]
 
 
-def validate(number, allow_temporary=False):
+def validate(number: str, allow_temporary: bool = False) -> str:
     """Check if the number is a valid HETU. It checks the format, whether a
     valid date is given and whether the check digit is correct. Allows
     temporary identifier range for individuals (900-999) if allow_temporary
@@ -104,7 +106,7 @@ def validate(number, allow_temporary=False):
     return number
 
 
-def is_valid(number, allow_temporary=False):
+def is_valid(number: str, allow_temporary: bool = False) -> bool:
     """Check if the number is a valid HETU."""
     try:
         return bool(validate(number, allow_temporary))
diff --git a/stdnum/fi/veronumero.py b/stdnum/fi/veronumero.py
index 4513df9..6b2a6ff 100644
--- a/stdnum/fi/veronumero.py
+++ b/stdnum/fi/veronumero.py
@@ -43,17 +43,19 @@ Traceback (most recent call last):
 InvalidLength: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the Veronumero to the minimal representation. This strips
     surrounding whitespace and removes separators."""
     return clean(number, ' ').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid tax number. This checks the length and
     formatting."""
     number = compact(number)
@@ -65,7 +67,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid tax number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/fi/ytunnus.py b/stdnum/fi/ytunnus.py
index 2f38241..b489830 100644
--- a/stdnum/fi/ytunnus.py
+++ b/stdnum/fi/ytunnus.py
@@ -33,23 +33,25 @@ InvalidChecksum: ...
 '2077474-0'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.fi import alv
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return alv.compact(number)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid business identifier. This checks the
     length, formatting and check digit."""
     return alv.validate(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid business identifier."""
     try:
         return bool(validate(number))
@@ -57,7 +59,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return number[:7] + '-' + number[7:]
diff --git a/stdnum/figi.py b/stdnum/figi.py
index de5d648..daecd79 100644
--- a/stdnum/figi.py
+++ b/stdnum/figi.py
@@ -37,17 +37,19 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip().upper()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digits for the number."""
     # we use the full alphabet for the check digit calculation
     alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
@@ -58,7 +60,7 @@ def calc_check_digit(number):
     return str((10 - sum(int(n) for n in number)) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid FIGI."""
     number = compact(number)
     if not all(x in '0123456789BCDFGHJKLMNPQRSTVWXYZ' for x in number):
@@ -76,7 +78,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid FIGI."""
     try:
         return bool(validate(number))
diff --git a/stdnum/fo/__init__.py b/stdnum/fo/__init__.py
index 3c9b274..251fe25 100644
--- a/stdnum/fo/__init__.py
+++ b/stdnum/fo/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Faroe Islands numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.fo import vn as vat  # noqa: F401
diff --git a/stdnum/fo/vn.py b/stdnum/fo/vn.py
index a634571..536a3f7 100644
--- a/stdnum/fo/vn.py
+++ b/stdnum/fo/vn.py
@@ -42,11 +42,13 @@ InvalidFormat: ...
 '602590'
 """  # noqa: E501
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -58,7 +60,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Faroe Islands V-number number.
 
     This checks the length and formatting.
@@ -71,7 +73,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Faroe Islands V-number number."""
     try:
         return bool(validate(number))
@@ -79,6 +81,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/fr/__init__.py b/stdnum/fr/__init__.py
index 14abaa5..abb5e23 100644
--- a/stdnum/fr/__init__.py
+++ b/stdnum/fr/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of French numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.fr import tva as vat  # noqa: F401
diff --git a/stdnum/fr/nif.py b/stdnum/fr/nif.py
index d043674..348a4b9 100644
--- a/stdnum/fr/nif.py
+++ b/stdnum/fr/nif.py
@@ -48,22 +48,24 @@ InvalidComponent: ...
 '30 23 217 600 053'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip()
 
 
-def calc_check_digits(number):
+def calc_check_digits(number: str) -> str:
     """Calculate the check digits for the number."""
     return '%03d' % (int(number[:10]) % 511)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid NIF."""
     number = compact(number)
     if not isdigits(number):
@@ -77,7 +79,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid NIF."""
     try:
         return bool(validate(number))
@@ -85,7 +87,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join((number[:2], number[2:4], number[4:7],
diff --git a/stdnum/fr/nir.py b/stdnum/fr/nir.py
index 5f8bf44..1eab080 100644
--- a/stdnum/fr/nir.py
+++ b/stdnum/fr/nir.py
@@ -61,17 +61,19 @@ InvalidLength: ...
 '2 95 10 99 126 111 93'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' .').strip().upper()
 
 
-def calc_check_digits(number):
+def calc_check_digits(number: str) -> str:
     """Calculate the check digits for the number."""
     department = number[5:7]
     if department == '2A':
@@ -81,7 +83,7 @@ def calc_check_digits(number):
     return '%02d' % (97 - (int(number[:13]) % 97))
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is valid. This checks the length
     and check digits."""
     number = compact(number)
@@ -96,7 +98,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is valid."""
     try:
         return bool(validate(number))
@@ -104,7 +106,7 @@ def is_valid(number):
         return False
 
 
-def format(number, separator=' '):
+def format(number: str, separator: str = ' ') -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return separator.join((
diff --git a/stdnum/fr/siren.py b/stdnum/fr/siren.py
index afd8368..e360747 100644
--- a/stdnum/fr/siren.py
+++ b/stdnum/fr/siren.py
@@ -36,6 +36,8 @@ InvalidChecksum: ...
 '46 443 121 975'
 """
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
@@ -47,13 +49,13 @@ from stdnum.util import clean, isdigits
 # https://avis-situation-sirene.insee.fr/
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' .').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid SIREN. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -65,7 +67,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid SIREN."""
     try:
         return bool(validate(number))
@@ -73,7 +75,7 @@ def is_valid(number):
         return False
 
 
-def to_tva(number):
+def to_tva(number: str) -> str:
     """Return a TVA that prepends the two extra check digits to the SIREN."""
     # note that this always returns numeric check digits
     # it is unclean when the alphabetic ones are used
diff --git a/stdnum/fr/siret.py b/stdnum/fr/siret.py
index 7e728f7..477d35b 100644
--- a/stdnum/fr/siret.py
+++ b/stdnum/fr/siret.py
@@ -47,19 +47,21 @@ InvalidChecksum: ...
 '732 829 320 00074'
 """
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.exceptions import *
 from stdnum.fr import siren
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' .').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid SIRET. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -78,7 +80,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid SIRET."""
     try:
         return bool(validate(number))
@@ -86,7 +88,7 @@ def is_valid(number):
         return False
 
 
-def to_siren(number):
+def to_siren(number: str) -> str:
     """Convert the SIRET number to a SIREN number.
 
     The SIREN number is the 9 first digits of the SIRET number.
@@ -101,7 +103,7 @@ def to_siren(number):
     return ''.join(_siren)
 
 
-def to_tva(number):
+def to_tva(number: str) -> str:
     """Convert the SIRET number to a TVA number.
 
     The TVA number is built from the SIREN number, prepended by two extra
@@ -110,7 +112,7 @@ def to_tva(number):
     return siren.to_tva(to_siren(number))
 
 
-def format(number, separator=' '):
+def format(number: str, separator: str = ' ') -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return separator.join((number[0:3], number[3:6], number[6:9], number[9:]))
diff --git a/stdnum/fr/tva.py b/stdnum/fr/tva.py
index b224aae..2638663 100644
--- a/stdnum/fr/tva.py
+++ b/stdnum/fr/tva.py
@@ -43,6 +43,8 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.fr import siren
 from stdnum.util import clean, isdigits
@@ -52,7 +54,7 @@ from stdnum.util import clean, isdigits
 _alphabet = '0123456789ABCDEFGHJKLMNPQRSTUVWXYZ'
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -.').upper().strip()
@@ -61,7 +63,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -93,7 +95,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/gb/nhs.py b/stdnum/gb/nhs.py
index ac6382d..0b11554 100644
--- a/stdnum/gb/nhs.py
+++ b/stdnum/gb/nhs.py
@@ -41,23 +41,25 @@ InvalidChecksum: ...
 '943 476 5870'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip()
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum. The checksum is only used for the 9 digits
     of the number and the result can either be 0 or 42."""
     return sum(i * int(n) for i, n in enumerate(reversed(number), 1)) % 11
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is valid. This checks the length and check
     digit."""
     number = compact(number)
@@ -70,7 +72,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is valid."""
     try:
         return bool(validate(number))
@@ -78,7 +80,7 @@ def is_valid(number):
         return False
 
 
-def format(number, separator=' '):
+def format(number: str, separator: str = ' ') -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return separator.join((number[0:3], number[3:6], number[6:]))
diff --git a/stdnum/gb/sedol.py b/stdnum/gb/sedol.py
index a9c2727..2b38b17 100644
--- a/stdnum/gb/sedol.py
+++ b/stdnum/gb/sedol.py
@@ -33,6 +33,8 @@ InvalidChecksum: ...
 'GB00B15KXQ89'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
@@ -41,20 +43,20 @@ from stdnum.util import clean, isdigits
 _alphabet = '0123456789 BCD FGH JKLMN PQRST VWXYZ'
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip().upper()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digits for the number."""
     weights = (1, 3, 1, 7, 3, 9)
     s = sum(w * _alphabet.index(n) for w, n in zip(weights, number))
     return str((10 - s) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is valid. This checks the length and check
     digit."""
     number = compact(number)
@@ -71,7 +73,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is valid."""
     try:
         return bool(validate(number))
@@ -79,7 +81,7 @@ def is_valid(number):
         return False
 
 
-def to_isin(number):
+def to_isin(number: str) -> str:
     """Convert the number to an ISIN."""
     from stdnum import isin
     return isin.from_natid('GB', number)
diff --git a/stdnum/gb/upn.py b/stdnum/gb/upn.py
index c0684be..0e9c94e 100644
--- a/stdnum/gb/upn.py
+++ b/stdnum/gb/upn.py
@@ -48,6 +48,8 @@ Traceback (most recent call last):
 InvalidComponent: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
@@ -72,20 +74,20 @@ _la_numbers = set((
     919, 921, 925, 926, 928, 929, 931, 933, 935, 936, 937, 938))
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').upper().strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for the number."""
     check = sum(i * _alphabet.index(n)
                 for i, n in enumerate(number[-12:], 2)) % 23
     return _alphabet[check]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid UPN. This checks length, formatting and
     check digits."""
     number = compact(number)
@@ -100,7 +102,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid UPN."""
     try:
         return bool(validate(number))
diff --git a/stdnum/gb/utr.py b/stdnum/gb/utr.py
index 1c4ee70..eab0bf5 100644
--- a/stdnum/gb/utr.py
+++ b/stdnum/gb/utr.py
@@ -35,24 +35,26 @@ Traceback (most recent call last):
 InvalidChecksum: ..
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').upper().strip().lstrip('K')
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for the number. The passed number should not
     have the check digit (the first one) included."""
     weights = (6, 7, 8, 9, 10, 5, 4, 3, 2)
     return '21987654321'[sum(int(n) * w for n, w in zip(number, weights)) % 11]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid UTR."""
     number = compact(number)
     if not isdigits(number):
@@ -64,7 +66,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid UTR."""
     try:
         return bool(validate(number))
diff --git a/stdnum/gb/vat.py b/stdnum/gb/vat.py
index c8f7086..3ee9f96 100644
--- a/stdnum/gb/vat.py
+++ b/stdnum/gb/vat.py
@@ -35,11 +35,13 @@ InvalidChecksum: ...
 '980 7806 84'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -.').upper().strip()
@@ -48,14 +50,14 @@ def compact(number):
     return number
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum. The checksum is only used for the 9 digits
     of the number and the result can either be 0 or 42."""
     weights = (8, 7, 6, 5, 4, 3, 2, 10, 1)
     return sum(w * int(n) for w, n in zip(weights, number)) % 97
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -100,7 +102,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
@@ -108,7 +110,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     if len(number) == 5:
diff --git a/stdnum/gh/__init__.py b/stdnum/gh/__init__.py
index 41922b4..1809b17 100644
--- a/stdnum/gh/__init__.py
+++ b/stdnum/gh/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Ghana numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.gh import tin as vat  # noqa: F401
diff --git a/stdnum/gh/tin.py b/stdnum/gh/tin.py
index 2b0130c..f9ea45a 100644
--- a/stdnum/gh/tin.py
+++ b/stdnum/gh/tin.py
@@ -47,6 +47,8 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """  # noqa: E501
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -56,7 +58,7 @@ from stdnum.util import clean
 _gh_tin_re = re.compile(r'^[PCGQV]{1}00[A-Z0-9]{8}$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -65,13 +67,13 @@ def compact(number):
     return clean(number, ' ').upper()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for the TIN."""
     check = sum((i + 1) * int(n) for i, n in enumerate(number[1:10])) % 11
     return 'X' if check == 10 else str(check)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Ghana TIN."""
     number = compact(number)
     if len(number) != 11:
@@ -83,7 +85,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Ghana TIN."""
     try:
         return bool(validate(number))
diff --git a/stdnum/gn/__init__.py b/stdnum/gn/__init__.py
index 263e886..136d748 100644
--- a/stdnum/gn/__init__.py
+++ b/stdnum/gn/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Guinea numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.gn import nifp as vat  # noqa: F401
diff --git a/stdnum/gn/nifp.py b/stdnum/gn/nifp.py
index f5ad688..d374931 100644
--- a/stdnum/gn/nifp.py
+++ b/stdnum/gn/nifp.py
@@ -44,12 +44,14 @@ InvalidChecksum: ...
 '693-770-885'
 """
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -58,7 +60,7 @@ def compact(number):
     return clean(number, ' -').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Guinea NIFp number.
 
     This checks the length, formatting and check digit.
@@ -72,7 +74,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Guinea NIFp number."""
     try:
         return bool(validate(number))
@@ -80,7 +82,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join([number[:3], number[3:-3], number[-3:]])
diff --git a/stdnum/gr/amka.py b/stdnum/gr/amka.py
index e04efeb..bfb1ae3 100644
--- a/stdnum/gr/amka.py
+++ b/stdnum/gr/amka.py
@@ -41,6 +41,8 @@ datetime.date(1930, 1, 1)
 'M'
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum import luhn
@@ -48,13 +50,13 @@ from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip()
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Split the date parts from the number and return the date of birth.
     Since only two digits are used for the year, the century may be
     incorrect."""
@@ -71,7 +73,7 @@ def get_birth_date(number):
             raise InvalidComponent()
 
 
-def get_gender(number):
+def get_gender(number: str) -> str:
     """Get the gender (M/F) from the person's AMKA."""
     number = compact(number)
     if int(number[9]) % 2:
@@ -80,7 +82,7 @@ def get_gender(number):
         return 'F'
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid AMKA. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -93,7 +95,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid AMKA."""
     try:
         return bool(validate(number))
diff --git a/stdnum/gr/vat.py b/stdnum/gr/vat.py
index dc13c0c..b83117e 100644
--- a/stdnum/gr/vat.py
+++ b/stdnum/gr/vat.py
@@ -32,11 +32,13 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -./:').upper().strip()
@@ -47,7 +49,7 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     checksum = 0
@@ -56,7 +58,7 @@ def calc_check_digit(number):
     return str(checksum * 2 % 11 % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -69,7 +71,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/grid.py b/stdnum/grid.py
index cb94879..b1ef26a 100644
--- a/stdnum/grid.py
+++ b/stdnum/grid.py
@@ -37,11 +37,13 @@ InvalidChecksum: ...
 'A1-2425G-ABC1234002-M'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the GRid to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').strip().upper()
@@ -50,7 +52,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid GRid."""
     from stdnum.iso7064 import mod_37_36
     number = compact(number)
@@ -59,7 +61,7 @@ def validate(number):
     return mod_37_36.validate(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid GRid."""
     try:
         return bool(validate(number))
@@ -67,8 +69,8 @@ def is_valid(number):
         return False
 
 
-def format(number, separator='-'):
+def format(number: str, separator: str = '-') -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
-    number = (number[0:2], number[2:7], number[7:17], number[17:])
-    return separator.join(x for x in number if x)
+    parts = (number[0:2], number[2:7], number[7:17], number[17:])
+    return separator.join(x for x in parts if x)
diff --git a/stdnum/gs1_128.py b/stdnum/gs1_128.py
index ba41051..38e6e47 100644
--- a/stdnum/gs1_128.py
+++ b/stdnum/gs1_128.py
@@ -47,6 +47,8 @@ More information:
 '013842587609507417181119371'
 """
 
+from __future__ import annotations
+
 import datetime
 import decimal
 import re
@@ -56,6 +58,12 @@ from stdnum.exceptions import *
 from stdnum.util import clean
 
 
+TYPE_CHECKING = False
+if TYPE_CHECKING:  # pragma: no cover (only used when type checking)
+    from collections.abc import Mapping
+    from typing import Any
+
+
 # our open copy of the application identifier database
 _gs1_aidb = numdb.get('gs1_ai')
 
@@ -68,7 +76,7 @@ _ai_validators = {
 }
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the GS1-128 to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -78,11 +86,12 @@ def compact(number):
     return clean(number, '()').strip()
 
 
-def _encode_value(fmt, _type, value):
+def _encode_value(fmt: str, _type: str, value: object) -> str:
     """Encode the specified value given the format and type."""
     if _type == 'decimal':
         if isinstance(value, (list, tuple)) and fmt.startswith('N3+'):
             number = _encode_value(fmt[3:], _type, value[1])
+            assert isinstance(value[0], str)
             return number[0] + value[0].rjust(3, '0') + number[1:]
         value = str(value)
         if fmt.startswith('N..'):
@@ -126,22 +135,25 @@ def _encode_value(fmt, _type, value):
     return str(value)
 
 
-def _max_length(fmt, _type):
+def _max_length(fmt: str, _type: str) -> int:
     """Determine the maximum length based on the format ad type."""
-    length = sum(int(re.match(r'^[NXY][0-9]*?[.]*([0-9]+)[\[\]]?$', 
x).group(1)) for x in fmt.split('+'))
+    length = sum(
+        int(re.match(r'^[NXY][0-9]*?[.]*([0-9]+)[\[\]]?$', x).group(1))  # 
type: ignore[misc, union-attr]
+        for x in fmt.split('+')
+    )
     if _type == 'decimal':
         length += 1
     return length
 
 
-def _pad_value(fmt, _type, value):
+def _pad_value(fmt: str, _type: str, value: str) -> str:
     """Pad the value to the maximum length for the format."""
     if _type in ('decimal', 'int'):
         return value.rjust(_max_length(fmt, _type), '0')
     return value.ljust(_max_length(fmt, _type))
 
 
-def _decode_value(fmt, _type, value):
+def _decode_value(fmt: str, _type: str, value: str) -> Any:
     """Decode the specified value given the fmt and type."""
     if _type == 'decimal':
         if fmt.startswith('N3+'):
@@ -173,7 +185,7 @@ def _decode_value(fmt, _type, value):
     return value.strip()
 
 
-def info(number, separator=''):
+def info(number: str, separator: str = '') -> dict[str, Any]:
     """Return a dictionary containing the information from the GS1-128 code.
 
     The returned dictionary maps application identifiers to values with the
@@ -214,7 +226,7 @@ def info(number, separator=''):
     return data
 
 
-def encode(data, separator='', parentheses=False):
+def encode(data: Mapping[str, object], separator: str = '', parentheses: bool 
= False) -> str:
     """Generate a GS1-128 for the application identifiers supplied.
 
     The provided dictionary is expected to map application identifiers to
@@ -259,7 +271,7 @@ def encode(data, separator='', parentheses=False):
         ])
 
 
-def validate(number, separator=''):
+def validate(number: str, separator: str = '') -> str:
     """Check if the number provided is a valid GS1-128.
 
     This checks formatting of the number and values and returns a stable
@@ -280,7 +292,7 @@ def validate(number, separator=''):
         raise InvalidFormat()
 
 
-def is_valid(number, separator=''):
+def is_valid(number: str, separator: str = '') -> bool:
     """Check if the number provided is a valid GS1-128."""
     try:
         return bool(validate(number))
diff --git a/stdnum/gt/__init__.py b/stdnum/gt/__init__.py
index c1f38d9..e0d6f06 100644
--- a/stdnum/gt/__init__.py
+++ b/stdnum/gt/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Guatemalan numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.gt import nit as vat  # noqa: F401
diff --git a/stdnum/gt/nit.py b/stdnum/gt/nit.py
index efbe1b0..38c1fe3 100644
--- a/stdnum/gt/nit.py
+++ b/stdnum/gt/nit.py
@@ -48,24 +48,26 @@ InvalidLength: ...
 '3952550-3'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').upper().strip().lstrip('0')
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     c = -sum(i * int(n) for i, n in enumerate(reversed(number), 2)) % 11
     return 'K' if c == 10 else str(c)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Guatemala NIT number.
 
     This checks the length, formatting and check digit.
@@ -82,7 +84,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Guatemala NIT number."""
     try:
         return bool(validate(number))
@@ -90,7 +92,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join([number[:-1], number[-1]])
diff --git a/stdnum/hr/__init__.py b/stdnum/hr/__init__.py
index 54a21c2..8d2e3d6 100644
--- a/stdnum/hr/__init__.py
+++ b/stdnum/hr/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Croatian numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.hr import oib as vat  # noqa: F401
diff --git a/stdnum/hr/oib.py b/stdnum/hr/oib.py
index 72801a4..ed176cc 100644
--- a/stdnum/hr/oib.py
+++ b/stdnum/hr/oib.py
@@ -32,12 +32,14 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_11_10
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').upper().strip()
@@ -46,7 +48,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid OIB number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -58,7 +60,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid OIB number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/hu/__init__.py b/stdnum/hu/__init__.py
index 58735a0..f271446 100644
--- a/stdnum/hu/__init__.py
+++ b/stdnum/hu/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Hungarian numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.hu import anum as vat  # noqa: F401
diff --git a/stdnum/hu/anum.py b/stdnum/hu/anum.py
index bb9b863..1316ed9 100644
--- a/stdnum/hu/anum.py
+++ b/stdnum/hu/anum.py
@@ -31,11 +31,13 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').upper().strip()
@@ -44,13 +46,13 @@ def compact(number):
     return number
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum. Valid numbers should have a checksum of 0."""
     weights = (9, 7, 3, 1, 9, 7, 3, 1)
     return sum(w * int(n) for w, n in zip(weights, number)) % 10
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -63,7 +65,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/iban.py b/stdnum/iban.py
index ccc5626..7c54871 100644
--- a/stdnum/iban.py
+++ b/stdnum/iban.py
@@ -44,12 +44,14 @@ More information:
 '31'
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum import numdb
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_97_10
-from stdnum.util import clean, get_cc_module
+from stdnum.util import NumberValidationModule, clean, get_cc_module
 
 
 # our open copy of the IBAN database
@@ -62,23 +64,23 @@ _struct_re = re.compile(r'([1-9][0-9]*)!([nac])')
 _country_modules = {}
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the iban number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -.').strip().upper()
 
 
-def calc_check_digits(number):
+def calc_check_digits(number: str) -> str:
     """Calculate the check digits that should be put in the number to make
     it valid. Check digits in the supplied number are ignored."""
     number = compact(number)
     return mod_97_10.calc_check_digits(number[4:] + number[:2])
 
 
-def _struct_to_re(structure):
+def _struct_to_re(structure: str) -> re.Pattern[str]:
     """Convert an IBAN structure to a regular expression that can be used
     to validate the number."""
-    def conv(match):
+    def conv(match: re.Match[str]) -> str:
         chars = {
             'n': '[0-9]',
             'a': '[A-Z]',
@@ -88,7 +90,7 @@ def _struct_to_re(structure):
     return re.compile('^%s$' % _struct_re.sub(conv, structure))
 
 
-def _get_cc_module(cc):
+def _get_cc_module(cc: str) -> NumberValidationModule | None:
     """Get the IBAN module based on the country code."""
     cc = cc.lower()
     if cc not in _country_modules:
@@ -96,7 +98,7 @@ def _get_cc_module(cc):
     return _country_modules[cc]
 
 
-def validate(number, check_country=True):
+def validate(number: str, check_country: bool = True) -> str:
     """Check if the number provided is a valid IBAN. The country-specific
     check can be disabled with the check_country argument."""
     number = compact(number)
@@ -119,7 +121,7 @@ def validate(number, check_country=True):
     return number
 
 
-def is_valid(number, check_country=True):
+def is_valid(number: str, check_country: bool = True) -> bool:
     """Check if the number provided is a valid IBAN."""
     try:
         return bool(validate(number, check_country=check_country))
@@ -127,7 +129,7 @@ def is_valid(number, check_country=True):
         return False
 
 
-def format(number, separator=' '):
+def format(number: str, separator: str = ' ') -> str:
     """Reformat the passed number to the space-separated format."""
     number = compact(number)
     return separator.join(number[i:i + 4] for i in range(0, len(number), 4))
diff --git a/stdnum/id/__init__.py b/stdnum/id/__init__.py
index a574bd5..86737d8 100644
--- a/stdnum/id/__init__.py
+++ b/stdnum/id/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Indonesian numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.id import npwp as vat  # noqa: F401
diff --git a/stdnum/id/nik.py b/stdnum/id/nik.py
index 18cbb0d..906a4cd 100644
--- a/stdnum/id/nik.py
+++ b/stdnum/id/nik.py
@@ -55,13 +55,15 @@ Traceback (most recent call last):
 InvalidComponent: ...
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes
@@ -70,7 +72,7 @@ def compact(number):
     return clean(number, ' -.').strip()
 
 
-def get_birth_date(number, minyear=1920):
+def get_birth_date(number: str, minyear: int = 1920) -> datetime.date:
     """Get the birth date from the person's NIK.
 
     Note that the number only encodes the last two digits of the year so
@@ -90,7 +92,7 @@ def get_birth_date(number, minyear=1920):
         raise InvalidComponent()
 
 
-def _check_registration_place(number):
+def _check_registration_place(number: str) -> dict[str, str]:
     """Use the number to look up the place of registration of the person."""
     from stdnum import numdb
     results = numdb.get('id/loc').info(number[:4])[0][1]
@@ -99,7 +101,7 @@ def _check_registration_place(number):
     return results
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Indonesian NIK."""
     number = compact(number)
     if not isdigits(number):
@@ -111,7 +113,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Indonesian NIK."""
     try:
         return bool(validate(number))
diff --git a/stdnum/id/npwp.py b/stdnum/id/npwp.py
index effa8ec..fc1d5b9 100644
--- a/stdnum/id/npwp.py
+++ b/stdnum/id/npwp.py
@@ -52,13 +52,15 @@ InvalidLength: ...
 '01.300.066.6-091.000'
 """  # noqa: E501
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.exceptions import *
 from stdnum.id import nik
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes
@@ -67,7 +69,7 @@ def compact(number):
     return clean(number, ' -.').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Indonesia NPWP number."""
     number = compact(number)
     if not isdigits(number):
@@ -86,7 +88,7 @@ def validate(number):
     raise InvalidLength()
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Indonesia NPWP number."""
     try:
         return bool(validate(number))
@@ -94,7 +96,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '%s.%s.%s.%s-%s.%s' % (
diff --git a/stdnum/ie/pps.py b/stdnum/ie/pps.py
index a52d8f3..c0b9f7c 100644
--- a/stdnum/ie/pps.py
+++ b/stdnum/ie/pps.py
@@ -51,6 +51,8 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -62,13 +64,13 @@ pps_re = re.compile(r'^\d{7}[A-W][ABHWTX]?$')
 """Regular expression used to check syntax of PPS numbers."""
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').upper().strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid PPS number. This checks the
     length, formatting and check digit."""
     number = compact(number)
@@ -85,7 +87,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid PPS number. This checks the
     length, formatting and check digit."""
     try:
diff --git a/stdnum/ie/vat.py b/stdnum/ie/vat.py
index 8db582c..66958a2 100644
--- a/stdnum/ie/vat.py
+++ b/stdnum/ie/vat.py
@@ -41,11 +41,13 @@ InvalidFormat: ...
 '0234561T'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').upper().strip()
@@ -57,7 +59,7 @@ def compact(number):
 _alphabet = 'WABCDEFGHIJKLMNOPQRSTUV'
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     number = compact(number).zfill(7)
@@ -66,7 +68,7 @@ def calc_check_digit(number):
         9 * _alphabet.index(number[7:])) % 23]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid VAT number. This checks the
     length, formatting and check digit."""
     number = compact(number)
@@ -89,7 +91,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid VAT number. This checks the
     length, formatting and check digit."""
     try:
@@ -98,7 +100,7 @@ def is_valid(number):
         return False
 
 
-def convert(number):
+def convert(number: str) -> str:
     """Convert an "old" style 8-digit VAT number where the second character
     is a letter to the new 8-digit format where only the last digit is a
     character."""
diff --git a/stdnum/il/__init__.py b/stdnum/il/__init__.py
index b002751..46809fa 100644
--- a/stdnum/il/__init__.py
+++ b/stdnum/il/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Israeli numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.il import hp as vat  # noqa: F401
diff --git a/stdnum/il/hp.py b/stdnum/il/hp.py
index a396c15..d65a32e 100644
--- a/stdnum/il/hp.py
+++ b/stdnum/il/hp.py
@@ -48,18 +48,20 @@ Traceback (most recent call last):
 InvalidComponent: ...
 """  # noqa: E501
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any separators and removes surrounding whitespace."""
     return clean(number, ' -').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid ID. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -73,7 +75,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid ID. This checks the length,
     formatting and check digit."""
     try:
@@ -82,6 +84,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/il/idnr.py b/stdnum/il/idnr.py
index 545f5fc..3f39ac4 100644
--- a/stdnum/il/idnr.py
+++ b/stdnum/il/idnr.py
@@ -43,18 +43,20 @@ Traceback (most recent call last):
 InvalidLength: ...
 """
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip().zfill(9)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid ID. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -66,7 +68,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid ID. This checks the length,
     formatting and check digit."""
     try:
@@ -75,7 +77,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return number[:-1] + '-' + number[-1:]
diff --git a/stdnum/imei.py b/stdnum/imei.py
index 2ea1210..d6451a4 100644
--- a/stdnum/imei.py
+++ b/stdnum/imei.py
@@ -46,18 +46,20 @@ InvalidChecksum: ...
 ('35686800', '004141', '')
 """
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the IMEI number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip().upper()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid IMEI (or IMEISV) number."""
     number = compact(number)
     if not isdigits(number):
@@ -71,7 +73,7 @@ def validate(number):
     return number
 
 
-def imei_type(number):
+def imei_type(number: str) -> str | None:
     """Check the passed number and return 'IMEI', 'IMEISV' or None (for
     invalid) for checking the type of number passed."""
     try:
@@ -84,7 +86,7 @@ def imei_type(number):
         return 'IMEISV'
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid IMEI (or IMEISV) number."""
     try:
         return bool(validate(number))
@@ -92,7 +94,7 @@ def is_valid(number):
         return False
 
 
-def split(number):
+def split(number: str) -> tuple[str, str, str]:
     """Split the number into a Type Allocation Code (TAC), serial number
     and either the checksum (for IMEI) or the software version number (for
     IMEISV)."""
@@ -100,10 +102,10 @@ def split(number):
     return (number[:8], number[8:14], number[14:])
 
 
-def format(number, separator='-', add_check_digit=False):
+def format(number: str, separator: str = '-', add_check_digit: bool = False) 
-> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     if len(number) == 14 and add_check_digit:
         number += luhn.calc_check_digit(number)
-    number = (number[:2], number[2:8], number[8:14], number[14:])
-    return separator.join(x for x in number if x)
+    parts = (number[:2], number[2:8], number[8:14], number[14:])
+    return separator.join(x for x in parts if x)
diff --git a/stdnum/imo.py b/stdnum/imo.py
index c4b380a..9c0ae9f 100644
--- a/stdnum/imo.py
+++ b/stdnum/imo.py
@@ -40,11 +40,13 @@ InvalidChecksum: ...
 'IMO 8814275'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' ').upper().strip()
@@ -53,12 +55,12 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digits for the number."""
     return str(sum(int(n) * (7 - i) for i, n in enumerate(number[:6])) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is valid. This checks the length and
     check digit."""
     number = compact(number)
@@ -71,7 +73,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is valid. This checks the length and
     check digit."""
     try:
@@ -80,6 +82,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return 'IMO ' + compact(number)
diff --git a/stdnum/imsi.py b/stdnum/imsi.py
index 952615b..67ccd71 100644
--- a/stdnum/imsi.py
+++ b/stdnum/imsi.py
@@ -39,17 +39,19 @@ InvalidComponent: ...
 'China'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the IMSI number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip().upper()
 
 
-def split(number):
+def split(number: str) -> tuple[str, ...]:
     """Split the specified IMSI into a Mobile Country Code (MCC), a Mobile
     Network Code (MNC), a Mobile Station Identification Number (MSIN)."""
     # clean up number
@@ -59,7 +61,7 @@ def split(number):
     return tuple(numdb.get('imsi').split(number))
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid IMSI."""
     number = compact(number)
     if not isdigits(number):
@@ -71,7 +73,7 @@ def validate(number):
     return number
 
 
-def info(number):
+def info(number: str) -> dict[str, str]:
     """Return a dictionary of data about the supplied number."""
     # clean up number
     number = compact(number)
@@ -88,7 +90,7 @@ def info(number):
     return info
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid IMSI."""
     try:
         return bool(validate(number))
diff --git a/stdnum/in_/__init__.py b/stdnum/in_/__init__.py
index ec870c8..ff608c3 100644
--- a/stdnum/in_/__init__.py
+++ b/stdnum/in_/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Indian numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.in_ import gstin as vat  # noqa: F401
diff --git a/stdnum/in_/aadhaar.py b/stdnum/in_/aadhaar.py
index cf5d97f..83f70df 100644
--- a/stdnum/in_/aadhaar.py
+++ b/stdnum/in_/aadhaar.py
@@ -58,6 +58,8 @@ InvalidFormat: ...
 'XXXX XXXX 2346'
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum import verhoeff
@@ -69,13 +71,13 @@ aadhaar_re = re.compile(r'^[2-9][0-9]{11}$')
 """Regular expression used to check syntax of Aadhaar numbers."""
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid Aadhaar number. This checks
     the length, formatting and check digit."""
     number = compact(number)
@@ -89,7 +91,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid Aadhaar number. This checks
     the length, formatting and check digit."""
     try:
@@ -98,13 +100,13 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join((number[:4], number[4:8], number[8:]))
 
 
-def mask(number):
+def mask(number: str) -> str:
     """Masks the first 8 digits as per Ministry of Electronics and
     Information Technology (MeitY) guidelines."""
     number = compact(number)
diff --git a/stdnum/in_/epic.py b/stdnum/in_/epic.py
index 14ec89d..cc99295 100644
--- a/stdnum/in_/epic.py
+++ b/stdnum/in_/epic.py
@@ -52,6 +52,8 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum import luhn
@@ -62,13 +64,13 @@ from stdnum.util import clean
 _EPIC_RE = re.compile(r'^[A-Z]{3}[0-9]{7}$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').upper().strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid EPIC number. This checks the
     length, formatting and checksum."""
     number = compact(number)
@@ -80,7 +82,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid EPIC number. This checks the
     length, formatting and checksum."""
     try:
diff --git a/stdnum/in_/gstin.py b/stdnum/in_/gstin.py
index 84d8c07..b39d055 100644
--- a/stdnum/in_/gstin.py
+++ b/stdnum/in_/gstin.py
@@ -59,6 +59,8 @@ InvalidChecksum: ...
 'Maharashtra'
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum import luhn
@@ -112,13 +114,13 @@ _STATE_CODES = {
 }
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').upper().strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid GSTIN. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -133,7 +135,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid GSTIN. This checks the length,
     formatting and check digit."""
     try:
@@ -142,13 +144,13 @@ def is_valid(number):
         return False
 
 
-def to_pan(number):
+def to_pan(number: str) -> str:
     """Convert the number to a PAN."""
     number = compact(number)
     return number[2:12]
 
 
-def info(number):
+def info(number: str) -> dict[str, str | int | None]:
     """Provide information that can be decoded locally from GSTIN (without
     API)."""
     number = validate(number)
diff --git a/stdnum/in_/pan.py b/stdnum/in_/pan.py
index f3aedbe..c4d378d 100644
--- a/stdnum/in_/pan.py
+++ b/stdnum/in_/pan.py
@@ -59,6 +59,8 @@ InvalidComponent: ...
 'Individual'
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -83,13 +85,13 @@ _pan_holder_types = {
 # Type 'K' may have been discontinued, not listed on Income Text Dept website.
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').upper().strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid PAN. This checks the length
     and formatting."""
     number = compact(number)
@@ -103,7 +105,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid PAN. This checks the length
     and formatting."""
     try:
@@ -112,7 +114,7 @@ def is_valid(number):
         return False
 
 
-def info(number):
+def info(number: str) -> dict[str, str]:
     """Provide information that can be decoded from the PAN."""
     number = compact(number)
     holder_type = _pan_holder_types.get(number[3])
@@ -125,7 +127,7 @@ def info(number):
     }
 
 
-def mask(number):
+def mask(number: str) -> str:
     """Mask the PAN as per Central Board of Direct Taxes (CBDT) masking
     standard."""
     number = compact(number)
diff --git a/stdnum/in_/vid.py b/stdnum/in_/vid.py
index 6142ed9..9193a0e 100644
--- a/stdnum/in_/vid.py
+++ b/stdnum/in_/vid.py
@@ -56,6 +56,8 @@ InvalidFormat: ...
 'XXXX XXXX  XXXX 2342'
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum import verhoeff
@@ -67,13 +69,13 @@ _vid_re = re.compile(r'^[2-9][0-9]{15}$')
 """Regular expression used to check syntax of VID numbers."""
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid VID number. This checks
     the length, formatting and check digit."""
     number = compact(number)
@@ -87,7 +89,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid VID number. This checks
     the length, formatting and check digit."""
     try:
@@ -96,13 +98,13 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join((number[:4], number[4:8], number[8:12], number[12:]))
 
 
-def mask(number):
+def mask(number: str) -> str:
     """Masks the first 8 digits as per Ministry of Electronics and
     Information Technology (MeitY) guidelines."""
     number = compact(number)
diff --git a/stdnum/is_/__init__.py b/stdnum/is_/__init__.py
index ef06147..aadfbd9 100644
--- a/stdnum/is_/__init__.py
+++ b/stdnum/is_/__init__.py
@@ -20,6 +20,8 @@
 
 """Collection of Icelandic numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.is_ import kennitala as personalid  # noqa: F401
 from stdnum.is_ import vsk as vat  # noqa: F401
diff --git a/stdnum/is_/kennitala.py b/stdnum/is_/kennitala.py
index e810915..386fc4e 100644
--- a/stdnum/is_/kennitala.py
+++ b/stdnum/is_/kennitala.py
@@ -40,6 +40,8 @@ InvalidComponent: ...
 '120174-3399'
 """
 
+from __future__ import annotations
+
 import datetime
 import re
 
@@ -58,20 +60,20 @@ _kennitala_re = re.compile(
     r'(?P<century>[09])$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the kennitala to the minimal representation. This
     strips surrounding whitespace and separation dash, and converts it
     to upper case."""
     return clean(number, '-').upper().strip()
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum."""
     weights = (3, 2, 7, 6, 5, 4, 3, 2, 1, 0)
     return sum(w * int(n) for w, n in zip(weights, number)) % 11
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid kennitala. It checks the
     format, whether a valid date is given and whether the check digit is
     correct."""
@@ -100,7 +102,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid HETU. It checks the format,
     whether a valid date is given and whether the check digit is correct."""
     try:
@@ -109,7 +111,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return number[:6] + '-' + number[6:]
diff --git a/stdnum/is_/vsk.py b/stdnum/is_/vsk.py
index 6fd6ddd..ecc644c 100644
--- a/stdnum/is_/vsk.py
+++ b/stdnum/is_/vsk.py
@@ -30,11 +30,13 @@ Traceback (most recent call last):
 InvalidLength: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' ').upper().strip()
@@ -43,7 +45,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid VAT number. This checks the
     length and formatting."""
     number = compact(number)
@@ -54,7 +56,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid VAT number. This checks the
     length and formatting."""
     try:
diff --git a/stdnum/isan.py b/stdnum/isan.py
index 4f98218..80e1400 100644
--- a/stdnum/isan.py
+++ b/stdnum/isan.py
@@ -47,12 +47,14 @@ InvalidChecksum: ...
 '<ISAN root="1881-66C7-3420" episode="6541" version="9F3A-0245" />'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_37_36
 from stdnum.util import clean
 
 
-def split(number):
+def split(number: str) -> tuple[str, str, str, str, str]:
     """Split the number into a root, an episode or part, a check digit a
     version and another check digit. If any of the parts are missing an empty
     string is returned."""
@@ -65,17 +67,17 @@ def split(number):
         return number[0:12], number[12:16], number[16:], '', ''
 
 
-def compact(number, strip_check_digits=True):
+def compact(number: str, strip_check_digits: bool = True) -> str:
     """Convert the ISAN to the minimal representation. This strips the number
     of any valid separators and removes surrounding whitespace. The check
     digits are removed by default."""
-    number = list(split(number))
+    parts = list(split(number))
     if strip_check_digits:
-        number[2] = number[4] = ''
-    return ''.join(number)
+        parts[2] = parts[4] = ''
+    return ''.join(parts)
 
 
-def validate(number, strip_check_digits=False, add_check_digits=False):
+def validate(number: str, strip_check_digits: bool = False, add_check_digits: 
bool = False) -> str:
     """Check if the number provided is a valid ISAN. If check digits are
     present in the number they are validated. If strip_check_digits is True
     any existing check digits will be removed (after checking). If
@@ -106,7 +108,7 @@ def validate(number, strip_check_digits=False, 
add_check_digits=False):
     return root + episode + check1 + version + check2
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid ISAN. If check digits are
     present in the number they are validated."""
     try:
@@ -115,7 +117,12 @@ def is_valid(number):
         return False
 
 
-def format(number, separator='-', strip_check_digits=False, 
add_check_digits=True):
+def format(
+    number: str,
+    separator: str = '-',
+    strip_check_digits: bool = False,
+    add_check_digits: bool = True,
+) -> str:
     """Reformat the number to the standard presentation format. If
     add_check_digits is True the check digit will be added if they are not
     present yet. If both strip_check_digits and add_check_digits are True the
@@ -127,30 +134,30 @@ def format(number, separator='-', 
strip_check_digits=False, add_check_digits=Tru
         check1 = mod_37_36.calc_check_digit(root + episode)
     if add_check_digits and not check2 and version:
         check2 = mod_37_36.calc_check_digit(root + episode + version)
-    number = [root[i:i + 4] for i in range(0, 12, 4)] + [episode]
+    parts = [root[i:i + 4] for i in range(0, 12, 4)] + [episode]
     if check1:
-        number.append(check1)
+        parts.append(check1)
     if version:
-        number.extend((version[0:4], version[4:]))
+        parts.extend((version[0:4], version[4:]))
     if check2:
-        number.append(check2)
-    return separator.join(number)
+        parts.append(check2)
+    return separator.join(parts)
 
 
-def to_binary(number):
+def to_binary(number: str) -> bytes:
     """Convert the number to its binary representation (without the check
     digits)."""
     from binascii import a2b_hex
     return a2b_hex(compact(number, strip_check_digits=True))
 
 
-def to_xml(number):
+def to_xml(number: str) -> str:
     """Return the XML form of the ISAN as a string."""
     number = format(number, strip_check_digits=True, add_check_digits=False)
     return '<ISAN root="%s" episode="%s" version="%s" />' % (
         number[0:14], number[15:19], number[20:])
 
 
-def to_urn(number):
+def to_urn(number: str) -> str:
     """Return the URN representation of the ISAN."""
     return 'URN:ISAN:' + format(number, add_check_digits=True)
diff --git a/stdnum/isbn.py b/stdnum/isbn.py
index 0a36661..795a3ec 100644
--- a/stdnum/isbn.py
+++ b/stdnum/isbn.py
@@ -61,12 +61,14 @@ InvalidChecksum: ...
 '1-85798-218-5'
 """
 
+from __future__ import annotations
+
 from stdnum import ean
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number, convert=False):
+def compact(number: str, convert: bool = False) -> str:
     """Convert the ISBN to the minimal representation. This strips the number
     of any valid ISBN separators and removes surrounding whitespace. If the
     convert parameter is True the number is also converted to ISBN-13
@@ -79,7 +81,7 @@ def compact(number, convert=False):
     return number
 
 
-def _calc_isbn10_check_digit(number):
+def _calc_isbn10_check_digit(number: str) -> str:
     """Calculate the ISBN check digit for 10-digit numbers. The number passed
     should not have the check digit included."""
     check = sum((i + 1) * int(n)
@@ -87,7 +89,7 @@ def _calc_isbn10_check_digit(number):
     return 'X' if check == 10 else str(check)
 
 
-def validate(number, convert=False):
+def validate(number: str, convert: bool = False) -> str:
     """Check if the number provided is a valid ISBN (either a legacy 10-digit
     one or a 13-digit one). This checks the length and the check digit but does
     not check if the group and publisher are valid (use split() for that)."""
@@ -108,7 +110,7 @@ def validate(number, convert=False):
     return number
 
 
-def isbn_type(number):
+def isbn_type(number: str) -> str | None:
     """Check the passed number and return 'ISBN13', 'ISBN10' or None (for
     invalid) for checking the type of number passed."""
     try:
@@ -121,7 +123,7 @@ def isbn_type(number):
         return 'ISBN13'
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid ISBN (either a legacy 10-digit
     one or a 13-digit one). This checks the length and the check digit but does
     not check if the group and publisher are valid (use split() for that)."""
@@ -131,7 +133,7 @@ def is_valid(number):
         return False
 
 
-def to_isbn13(number):
+def to_isbn13(number: str) -> str:
     """Convert the number to ISBN-13 format."""
     number = number.strip()
     min_number = clean(number, ' -')
@@ -150,7 +152,7 @@ def to_isbn13(number):
         return '978' + number
 
 
-def to_isbn10(number):
+def to_isbn10(number: str) -> str:
     """Convert the number to ISBN-10 format."""
     number = number.strip()
     min_number = compact(number, convert=False)
@@ -172,7 +174,7 @@ def to_isbn10(number):
         return number + digit
 
 
-def split(number, convert=False):
+def split(number: str, convert: bool = False) -> tuple[str, str, str, str, 
str]:
     """Split the specified ISBN into an EAN.UCC prefix, a group prefix, a
     registrant, an item number and a check digit. If the number is in ISBN-10
     format the returned EAN.UCC prefix is '978'. If the convert parameter is
@@ -195,7 +197,7 @@ def split(number, convert=False):
     return ('' if delprefix else prefix, group, publisher, itemnr, number[-1])
 
 
-def format(number, separator='-', convert=False):
+def format(number: str, separator: str = '-', convert: bool = False) -> str:
     """Reformat the number to the standard presentation format with the
     EAN.UCC prefix (if any), the group prefix, the registrant, the item
     number and the check digit separated (if possible) by the specified
diff --git a/stdnum/isil.py b/stdnum/isil.py
index fb8ab8e..85081ea 100644
--- a/stdnum/isil.py
+++ b/stdnum/isil.py
@@ -55,6 +55,8 @@ InvalidComponent: ...
 'IT-RM0267'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean
 
@@ -64,13 +66,13 @@ _alphabet = set(
     '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-:/')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the ISIL to the minimal representation. This strips
     surrounding whitespace."""
     return clean(number, '').strip()
 
 
-def _is_known_agency(agency):
+def _is_known_agency(agency: str) -> bool:
     """Check whether the specified agency is valid."""
     # look it up in the db
     from stdnum import numdb
@@ -79,7 +81,7 @@ def _is_known_agency(agency):
     return len(results) == 1 and bool(results[0][1])
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid ISIL."""
     number = compact(number)
     if not all(x in _alphabet for x in number):
@@ -91,7 +93,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid ISIL."""
     try:
         return bool(validate(number))
@@ -99,7 +101,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     parts = number.split('-')
diff --git a/stdnum/isin.py b/stdnum/isin.py
index 1d48f88..7c5a9ee 100644
--- a/stdnum/isin.py
+++ b/stdnum/isin.py
@@ -41,6 +41,8 @@ InvalidChecksum: ...
 'GB00BYXJL758'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean
 
@@ -88,13 +90,13 @@ _country_codes = set(_iso_3116_1_country_codes + [
 _alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip().upper()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digits for the number."""
     # convert to numeric first, then double some, then sum individual digits
     number = ''.join(str(_alphabet.index(n)) for n in number)
@@ -103,7 +105,7 @@ def calc_check_digit(number):
     return str((10 - sum(int(n) for n in number)) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is valid. This checks the length and
     check digit."""
     number = compact(number)
@@ -118,7 +120,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is valid. This checks the length and
     check digit."""
     try:
@@ -127,7 +129,7 @@ def is_valid(number):
         return False
 
 
-def from_natid(country_code, number):
+def from_natid(country_code: str, number: str) -> str:
     """Generate an ISIN from a national security identifier."""
     number = country_code.upper() + compact(number).zfill(9)
     return number + calc_check_digit(number)
diff --git a/stdnum/ismn.py b/stdnum/ismn.py
index bedc31d..3b07a09 100644
--- a/stdnum/ismn.py
+++ b/stdnum/ismn.py
@@ -42,18 +42,20 @@ InvalidChecksum: ...
 '9790230671187'
 """
 
+from __future__ import annotations
+
 from stdnum import ean
 from stdnum.exceptions import *
 from stdnum.util import clean
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the ISMN to the minimal representation. This strips the number
     of any valid ISMN separators and removes surrounding whitespace."""
     return clean(number, ' -.').strip().upper()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid ISMN (either a legacy 10-digit
     one or a 13-digit one). This checks the length and the check bit but does
     not check if the publisher is known."""
@@ -71,7 +73,7 @@ def validate(number):
     return number
 
 
-def ismn_type(number):
+def ismn_type(number: str) -> str | None:
     """Check the type of ISMN number passed and return 'ISMN13', 'ISMN10'
     or None (for invalid)."""
     try:
@@ -84,7 +86,7 @@ def ismn_type(number):
         return 'ISMN13'
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid ISMN (either a legacy 10-digit
     one or a 13-digit one). This checks the length and the check bit but does
     not check if the publisher is known."""
@@ -94,7 +96,7 @@ def is_valid(number):
         return False
 
 
-def to_ismn13(number):
+def to_ismn13(number: str) -> str:
     """Convert the number to ISMN13 (EAN) format."""
     number = number.strip()
     min_number = compact(number)
@@ -115,7 +117,7 @@ _ranges = (
     (6, '700000', '899999'), (7, '9000000', '9999999'))
 
 
-def split(number):
+def split(number: str) -> tuple[str, str, str, str, str]:
     """Split the specified ISMN into a bookland prefix (979), an ISMN
     prefix (0), a publisher element (3 to 7 digits), an item element (2 to
     6 digits) and a check digit."""
@@ -126,9 +128,10 @@ def split(number):
         if low <= number[4:4 + length] <= high:
             return (number[:3], number[3], number[4:4 + length],
                     number[4 + length:-1], number[-1])
+    raise AssertionError('unreachable')  # pragma: no cover
 
 
-def format(number, separator='-'):
+def format(number: str, separator: str = '-') -> str:
     """Reformat the number to the standard presentation format with the
     prefixes, the publisher element, the item element and the check-digit
     separated by the specified separator. The number is converted to the
diff --git a/stdnum/isni.py b/stdnum/isni.py
index 5b07a01..8fac961 100644
--- a/stdnum/isni.py
+++ b/stdnum/isni.py
@@ -43,19 +43,20 @@ InvalidLength: ...
 '0000 0001 2281 955X'
 """
 
+from __future__ import annotations
 
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_11_2
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the ISNI to the minimal representation. This strips the number
     of any valid ISNI separators and removes surrounding whitespace."""
     return clean(number, ' -').strip().upper()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid ISNI. This checks the length and
     whether the check digit is correct."""
     number = compact(number)
@@ -67,7 +68,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid ISNI."""
     try:
         return bool(validate(number))
@@ -75,7 +76,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join((number[:4], number[4:8], number[8:12], number[12:16]))
diff --git a/stdnum/iso11649.py b/stdnum/iso11649.py
index d0b784f..2d94066 100644
--- a/stdnum/iso11649.py
+++ b/stdnum/iso11649.py
@@ -45,18 +45,20 @@ InvalidChecksum: ...
 'RF18 5390 0754 7034'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_97_10
 from stdnum.util import clean
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any invalid separators and removes surrounding whitespace."""
     return clean(number, ' -.,/:').upper().strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid ISO 11649 structured creditor
     reference number."""
     number = compact(number)
@@ -68,7 +70,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid ISO 11649 structured creditor
     number. This checks the length, formatting and check digits."""
     try:
@@ -77,7 +79,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Format the number provided for output.
 
     Blocks of 4 characters, the last block can be less than 4 characters. See
diff --git a/stdnum/iso6346.py b/stdnum/iso6346.py
index 225b8a9..1ab0afe 100644
--- a/stdnum/iso6346.py
+++ b/stdnum/iso6346.py
@@ -42,6 +42,8 @@ InvalidChecksum: ...
 'TASU 117000 0'
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -51,13 +53,13 @@ from stdnum.util import clean
 _iso6346_re = re.compile(r'^\w{3}(U|J|Z|R)\d{7}$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip().upper()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate check digit and return it for the 10 digit owner code and
     serial number."""
     number = compact(number)
@@ -67,7 +69,7 @@ def calc_check_digit(number):
         for i, n in enumerate(number)) % 11 % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Validate the given number (unicode) for conformity to ISO 6346."""
     number = compact(number)
     if len(number) != 11:
@@ -79,7 +81,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check whether the number conforms to the standard ISO6346. Unlike
     the validate function, this will not raise ValidationError(s)."""
     try:
@@ -88,7 +90,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join((number[:4], number[4:-1], number[-1:]))
diff --git a/stdnum/iso7064/mod_11_10.py b/stdnum/iso7064/mod_11_10.py
index 32f52b1..021572a 100644
--- a/stdnum/iso7064/mod_11_10.py
+++ b/stdnum/iso7064/mod_11_10.py
@@ -35,10 +35,12 @@ For a module that can do generic Mod x+1, x calculations 
see the
 '002006673085'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum. A valid number should have a checksum of 1."""
     check = 5
     for n in number:
@@ -46,13 +48,13 @@ def checksum(number):
     return check
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the extra digit that should be appended to the number to
     make it a valid number."""
     return str((1 - ((checksum(number) or 10) * 2) % 11) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check whether the check digit is valid."""
     try:
         valid = checksum(number) == 1
@@ -63,7 +65,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check whether the check digit is valid."""
     try:
         return bool(validate(number))
diff --git a/stdnum/iso7064/mod_11_2.py b/stdnum/iso7064/mod_11_2.py
index 166162d..736ae4d 100644
--- a/stdnum/iso7064/mod_11_2.py
+++ b/stdnum/iso7064/mod_11_2.py
@@ -37,10 +37,12 @@ For a module that can do generic Mod x, 2 calculations see 
the
 1
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum. A valid number should have a checksum of 1."""
     check = 0
     for n in number:
@@ -48,14 +50,14 @@ def checksum(number):
     return check
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the extra digit that should be appended to the number to
     make it a valid number."""
     c = (1 - 2 * checksum(number)) % 11
     return 'X' if c == 10 else str(c)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check whether the check digit is valid."""
     try:
         valid = checksum(number) == 1
@@ -66,7 +68,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check whether the check digit is valid."""
     try:
         return bool(validate(number))
diff --git a/stdnum/iso7064/mod_37_2.py b/stdnum/iso7064/mod_37_2.py
index df740ff..289370e 100644
--- a/stdnum/iso7064/mod_37_2.py
+++ b/stdnum/iso7064/mod_37_2.py
@@ -40,10 +40,12 @@ algorithm. For example Mod 11, 2:
 1
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 
 
-def checksum(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*'):
+def checksum(number: str, alphabet: str = 
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*') -> int:
     """Calculate the checksum. A valid number should have a checksum of 1."""
     modulus = len(alphabet)
     check = 0
@@ -52,14 +54,14 @@ def checksum(number, 
alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*'):
     return check
 
 
-def calc_check_digit(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*'):
+def calc_check_digit(number: str, alphabet: str = 
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*') -> str:
     """Calculate the extra digit that should be appended to the number to
     make it a valid number."""
     modulus = len(alphabet)
     return alphabet[(1 - 2 * checksum(number, alphabet)) % modulus]
 
 
-def validate(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*'):
+def validate(number: str, alphabet: str = 
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*') -> str:
     """Check whether the check digit is valid."""
     try:
         valid = checksum(number, alphabet) == 1
@@ -70,7 +72,7 @@ def validate(number, 
alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*'):
     return number
 
 
-def is_valid(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*'):
+def is_valid(number: str, alphabet: str = 
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*') -> bool:
     """Check whether the check digit is valid."""
     try:
         return bool(validate(number, alphabet))
diff --git a/stdnum/iso7064/mod_37_36.py b/stdnum/iso7064/mod_37_36.py
index d05531f..687eee6 100644
--- a/stdnum/iso7064/mod_37_36.py
+++ b/stdnum/iso7064/mod_37_36.py
@@ -38,10 +38,12 @@ algorithm. For example Mod 11, 10:
 '002006673085'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 
 
-def checksum(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
+def checksum(number: str, alphabet: str = 
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ') -> int:
     """Calculate the checksum. A valid number should have a checksum of 1."""
     modulus = len(alphabet)
     check = modulus // 2
@@ -50,14 +52,14 @@ def checksum(number, 
alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
     return check
 
 
-def calc_check_digit(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
+def calc_check_digit(number: str, alphabet: str = 
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ') -> str:
     """Calculate the extra digit that should be appended to the number to
     make it a valid number."""
     modulus = len(alphabet)
     return alphabet[(1 - ((checksum(number, alphabet) or modulus) * 2) % 
(modulus + 1)) % modulus]
 
 
-def validate(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
+def validate(number: str, alphabet: str = 
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ') -> str:
     """Check whether the check digit is valid."""
     try:
         valid = checksum(number, alphabet) == 1
@@ -68,7 +70,7 @@ def validate(number, 
alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
     return number
 
 
-def is_valid(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
+def is_valid(number: str, alphabet: str = 
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ') -> bool:
     """Check whether the check digit is valid."""
     try:
         return bool(validate(number, alphabet))
diff --git a/stdnum/iso7064/mod_97_10.py b/stdnum/iso7064/mod_97_10.py
index 21111c3..4c946c4 100644
--- a/stdnum/iso7064/mod_97_10.py
+++ b/stdnum/iso7064/mod_97_10.py
@@ -34,27 +34,29 @@ valid if the number modulo 97 is 1. As such it has two 
check digits.
 '35'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 
 
-def _to_base10(number):
+def _to_base10(number: str) -> str:
     """Prepare the number to its base10 representation."""
     return ''.join(
         str(int(x, 36)) for x in number)
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum. A valid number should have a checksum of 1."""
     return int(_to_base10(number)) % 97
 
 
-def calc_check_digits(number):
+def calc_check_digits(number: str) -> str:
     """Calculate the extra digits that should be appended to the number to
     make it a valid number."""
     return '%02d' % (98 - checksum(number + '00'))
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check whether the check digit is valid."""
     try:
         valid = checksum(number) == 1
@@ -65,7 +67,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check whether the check digit is valid."""
     try:
         return bool(validate(number))
diff --git a/stdnum/isrc.py b/stdnum/isrc.py
index 5fc4e01..b71c3bd 100644
--- a/stdnum/isrc.py
+++ b/stdnum/isrc.py
@@ -36,6 +36,8 @@ Traceback (most recent call last):
 InvalidComponent: ...
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -74,13 +76,13 @@ _country_codes = set(_iso_3116_1_country_codes + [
 ])
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the ISRC to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip().upper()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid ISRC. This checks the length,
     the alphabet, and the country code but does not check if the registrant
     code is known."""
@@ -95,7 +97,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid ISRC."""
     try:
         return bool(validate(number))
@@ -103,7 +105,7 @@ def is_valid(number):
         return False
 
 
-def format(number, separator='-'):
+def format(number: str, separator: str = '-') -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return separator.join((number[0:2], number[2:5], number[5:7], number[7:]))
diff --git a/stdnum/issn.py b/stdnum/issn.py
index 7b821a7..e2960ac 100644
--- a/stdnum/issn.py
+++ b/stdnum/issn.py
@@ -49,18 +49,20 @@ InvalidLength: ...
 '9770264359008'
 """
 
+from __future__ import annotations
+
 from stdnum import ean
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the ISSN to the minimal representation. This strips the number
     of any valid ISSN separators and removes surrounding whitespace."""
     return clean(number, ' -').strip().upper()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the ISSN check digit for 8-digit numbers. The number passed
     should not have the check digit included."""
     check = (11 - sum((8 - i) * int(n)
@@ -68,7 +70,7 @@ def calc_check_digit(number):
     return 'X' if check == 10 else str(check)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid ISSN. This checks the length and
     whether the check digit is correct."""
     number = compact(number)
@@ -81,7 +83,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid ISSN."""
     try:
         return bool(validate(number))
@@ -89,13 +91,13 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return number[:4] + '-' + number[4:]
 
 
-def to_ean(number, issue_code='00'):
+def to_ean(number: str, issue_code: str = '00') -> str:
     """Convert the number to EAN-13 format."""
     number = '977' + validate(number)[:-1] + issue_code
     return number + ean.calc_check_digit(number)
diff --git a/stdnum/it/__init__.py b/stdnum/it/__init__.py
index dbf2479..89dca80 100644
--- a/stdnum/it/__init__.py
+++ b/stdnum/it/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Italian numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.it import iva as vat  # noqa: F401
diff --git a/stdnum/it/aic.py b/stdnum/it/aic.py
index 777f98d..0e29c9a 100644
--- a/stdnum/it/aic.py
+++ b/stdnum/it/aic.py
@@ -47,6 +47,8 @@ More information:
 '000307052'
 """  # noqa: E501
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
@@ -55,12 +57,12 @@ from stdnum.util import clean, isdigits
 _base32_alphabet = '0123456789BCDFGHJKLMNPQRSTUVWXYZ'
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation."""
     return clean(number, ' ').upper().strip()
 
 
-def from_base32(number):
+def from_base32(number: str) -> str:
     """Convert a BASE32 representation of an AIC to a BASE10 one."""
     number = compact(number)
     if not all(x in _base32_alphabet for x in number):
@@ -70,7 +72,7 @@ def from_base32(number):
     return str(s).zfill(9)
 
 
-def to_base32(number):
+def to_base32(number: str) -> str:
     """Convert a BASE10 representation of an AIC to a BASE32 one."""
     number = compact(number)
     if not isdigits(number):
@@ -84,7 +86,7 @@ def to_base32(number):
     return res.zfill(6)
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for the BASE10 AIC code."""
     number = compact(number)
     weights = (1, 2, 1, 2, 1, 2, 1, 2)
@@ -92,7 +94,7 @@ def calc_check_digit(number):
                for x in (w * int(n) for w, n in zip(weights, number))) % 10)
 
 
-def validate_base10(number):
+def validate_base10(number: str) -> str:
     """Check if a string is a valid BASE10 representation of an AIC."""
     number = compact(number)
     if len(number) != 9:
@@ -106,7 +108,7 @@ def validate_base10(number):
     return number
 
 
-def validate_base32(number):
+def validate_base32(number: str) -> str:
     """Check if a string is a valid BASE32 representation of an AIC."""
     number = compact(number)
     if len(number) != 6:
@@ -114,7 +116,7 @@ def validate_base32(number):
     return validate_base10(from_base32(number))
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if a string is a valid AIC. BASE10 is the canonical form and
     is 9 chars long, while BASE32 is 6 chars."""
     number = compact(number)
@@ -124,7 +126,7 @@ def validate(number):
         return validate_base10(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the given string is a valid AIC code."""
     try:
         return bool(validate(number))
diff --git a/stdnum/it/codicefiscale.py b/stdnum/it/codicefiscale.py
index b38022c..e687c8d 100644
--- a/stdnum/it/codicefiscale.py
+++ b/stdnum/it/codicefiscale.py
@@ -55,6 +55,8 @@ InvalidChecksum: ...
 'H'
 """
 
+from __future__ import annotations
+
 import datetime
 import re
 
@@ -91,13 +93,13 @@ _odd_values.update(
 del values
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -:').strip().upper()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Compute the control code for the given personal number. The passed
     number should be the first 15 characters of a fiscal code."""
     code = sum(_odd_values[x] if n % 2 == 0 else _even_values[x]
@@ -105,7 +107,7 @@ def calc_check_digit(number):
     return 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[code % 26]
 
 
-def get_birth_date(number, minyear=1920):
+def get_birth_date(number: str, minyear: int = 1920) -> datetime.date:
     """Get the birth date from the person's fiscal code.
 
     Only the last two digits of the year are stored in the number. The dates
@@ -132,7 +134,7 @@ def get_birth_date(number, minyear=1920):
         raise InvalidComponent()
 
 
-def get_gender(number):
+def get_gender(number: str) -> str:
     """Get the gender of the person's fiscal code.
 
     >>> get_gender('RCCMNL83S18D969H')
@@ -146,7 +148,7 @@ def get_gender(number):
     return 'M' if int(number[9:11]) < 32 else 'F'
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the given fiscal code is valid. This checks the length and
     whether the check digit is correct."""
     number = compact(number)
@@ -163,7 +165,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the given fiscal code is valid."""
     try:
         return bool(validate(number))
diff --git a/stdnum/it/iva.py b/stdnum/it/iva.py
index 5c7aecd..f402765 100644
--- a/stdnum/it/iva.py
+++ b/stdnum/it/iva.py
@@ -34,12 +34,14 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -:').upper().strip()
@@ -48,7 +50,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -63,7 +65,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/jp/__init__.py b/stdnum/jp/__init__.py
index 9b99e0e..077cd25 100644
--- a/stdnum/jp/__init__.py
+++ b/stdnum/jp/__init__.py
@@ -19,4 +19,7 @@
 # 02110-1301 USA
 
 """Collection of Japanese numbers."""
+
+from __future__ import annotations
+
 from stdnum.jp import cn as vat  # noqa: F401
diff --git a/stdnum/jp/cn.py b/stdnum/jp/cn.py
index ff31e35..b2f7a3f 100644
--- a/stdnum/jp/cn.py
+++ b/stdnum/jp/cn.py
@@ -40,17 +40,19 @@ InvalidChecksum: ...
 '5-8356-7825-6246'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, '- ').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have
     the check digit included."""
     weights = (1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2)
@@ -58,7 +60,7 @@ def calc_check_digit(number):
     return str(9 - s)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is valid. This checks the length and check
     digit."""
     number = compact(number)
@@ -71,7 +73,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid CN."""
     try:
         return bool(validate(number))
@@ -79,7 +81,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join(
diff --git a/stdnum/ke/__init__.py b/stdnum/ke/__init__.py
index 1b33710..de4b0d7 100644
--- a/stdnum/ke/__init__.py
+++ b/stdnum/ke/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Kenyan numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.ke import pin as vat  # noqa: F401
diff --git a/stdnum/ke/pin.py b/stdnum/ke/pin.py
index 82e4918..e6262de 100644
--- a/stdnum/ke/pin.py
+++ b/stdnum/ke/pin.py
@@ -50,6 +50,8 @@ InvalidFormat: ...
 'A004416331M'
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -62,13 +64,13 @@ from stdnum.util import clean
 _pin_re = re.compile(r'^[A|P]{1}[0-9]{9}[A-Z]{1}$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip().upper()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Kenya PIN number.
 
     This checks the length and formatting.
@@ -82,7 +84,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Kenya PIN number."""
     try:
         return bool(validate(number))
@@ -90,6 +92,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/kr/__init__.py b/stdnum/kr/__init__.py
index b8d75f2..ed5da97 100644
--- a/stdnum/kr/__init__.py
+++ b/stdnum/kr/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of South Korean numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.kr import brn as vat  # noqa: F401
diff --git a/stdnum/kr/brn.py b/stdnum/kr/brn.py
index ccb997c..7f878fc 100644
--- a/stdnum/kr/brn.py
+++ b/stdnum/kr/brn.py
@@ -44,11 +44,13 @@ InvalidLength: ...
 '134-86-72683'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -57,7 +59,7 @@ def compact(number):
     return clean(number, ' -').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid South Korea BRN number.
 
     This checks the length and formatting.
@@ -72,7 +74,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid South Korea BRN number."""
     try:
         return bool(validate(number))
@@ -80,7 +82,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join([number[:3], number[3:5], number[5:]])
diff --git a/stdnum/kr/rrn.py b/stdnum/kr/rrn.py
index 804a6c1..cffa1e6 100644
--- a/stdnum/kr/rrn.py
+++ b/stdnum/kr/rrn.py
@@ -45,26 +45,28 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, '-').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     weights = (2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5)
     check = sum(w * int(n) for w, n in zip(weights, number))
     return str((11 - (check % 11)) % 10)
 
 
-def get_birth_date(number, allow_future=True):
+def get_birth_date(number: str, allow_future: bool = True) -> datetime.date:
     """Split the date parts from the number and return the birth date. If
     allow_future is False birth dates in the future are rejected."""
     number = compact(number)
@@ -90,7 +92,7 @@ def get_birth_date(number, allow_future=True):
     return date_of_birth
 
 
-def validate(number, allow_future=True):
+def validate(number: str, allow_future: bool = True) -> str:
     """Check if the number is a valid RNN. This checks the length, formatting
     and check digit. If allow_future is False birth dates in the future are
     rejected."""
@@ -110,7 +112,7 @@ def validate(number, allow_future=True):
     return number
 
 
-def is_valid(number, allow_future=True):
+def is_valid(number: str, allow_future: bool = True) -> bool:
     """Check if the number provided is valid."""
     try:
         return bool(validate(number, allow_future))
@@ -118,7 +120,7 @@ def is_valid(number, allow_future=True):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     if len(number) == 13:
diff --git a/stdnum/lei.py b/stdnum/lei.py
index 057404b..dc16718 100644
--- a/stdnum/lei.py
+++ b/stdnum/lei.py
@@ -40,18 +40,20 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_97_10
 from stdnum.util import clean
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding white space."""
     return clean(number, ' -').strip().upper()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is valid. This checks the length, format and check
     digits."""
     number = compact(number)
@@ -59,7 +61,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is valid."""
     try:
         return bool(validate(number))
diff --git a/stdnum/li/peid.py b/stdnum/li/peid.py
index 4ed228c..aad888b 100644
--- a/stdnum/li/peid.py
+++ b/stdnum/li/peid.py
@@ -39,17 +39,19 @@ Traceback (most recent call last):
 InvalidLength: The number has an invalid length.
 """  # noqa: E501
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' .').strip().lstrip('0')
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the given fiscal code is valid."""
     number = compact(number)
     if len(number) < 4 or len(number) > 12:
@@ -60,7 +62,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the given fiscal code is valid."""
     try:
         return bool(validate(number))
diff --git a/stdnum/lt/__init__.py b/stdnum/lt/__init__.py
index 3a02b29..f6ab3a6 100644
--- a/stdnum/lt/__init__.py
+++ b/stdnum/lt/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Lithuanian numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.lt import pvm as vat  # noqa: F401
diff --git a/stdnum/lt/asmens.py b/stdnum/lt/asmens.py
index a45dcdf..2563617 100644
--- a/stdnum/lt/asmens.py
+++ b/stdnum/lt/asmens.py
@@ -37,18 +37,20 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.ee.ik import calc_check_digit, get_birth_date
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip()
 
 
-def validate(number, validate_birth_date=True):
+def validate(number: str, validate_birth_date: bool = True) -> str:
     """Check if the number provided is valid. This checks the length,
     formatting, embedded date and check digit."""
     number = compact(number)
@@ -63,7 +65,7 @@ def validate(number, validate_birth_date=True):
     return number
 
 
-def is_valid(number, validate_birth_date=True):
+def is_valid(number: str, validate_birth_date: bool = True) -> bool:
     """Check if the number provided is valid. This checks the length,
     formatting, embedded date and check digit."""
     try:
diff --git a/stdnum/lt/pvm.py b/stdnum/lt/pvm.py
index a0984f4..699dd57 100644
--- a/stdnum/lt/pvm.py
+++ b/stdnum/lt/pvm.py
@@ -37,11 +37,13 @@ InvalidChecksum: ...
 '100004801610'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').upper().strip()
@@ -50,7 +52,7 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     check = sum((1 + i % 9) * int(n) for i, n in enumerate(number)) % 11
@@ -59,7 +61,7 @@ def calc_check_digit(number):
     return str(check % 11 % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -80,7 +82,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/lu/__init__.py b/stdnum/lu/__init__.py
index 3c28de8..35376af 100644
--- a/stdnum/lu/__init__.py
+++ b/stdnum/lu/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Luxembourgian numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.lu import tva as vat  # noqa: F401
diff --git a/stdnum/lu/tva.py b/stdnum/lu/tva.py
index 4ec8094..89f4b8a 100644
--- a/stdnum/lu/tva.py
+++ b/stdnum/lu/tva.py
@@ -32,11 +32,13 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' :.-').upper().strip()
@@ -45,12 +47,12 @@ def compact(number):
     return number
 
 
-def calc_check_digits(number):
+def calc_check_digits(number: str) -> str:
     """Calculate the check digits for the number."""
     return '%02d' % (int(number) % 89)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -63,7 +65,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/luhn.py b/stdnum/luhn.py
index e41ce78..7dcbf89 100644
--- a/stdnum/luhn.py
+++ b/stdnum/luhn.py
@@ -44,21 +44,23 @@ InvalidChecksum: ...
 14
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 
 
-def checksum(number, alphabet='0123456789'):
+def checksum(number: str, alphabet: str = '0123456789') -> int:
     """Calculate the Luhn checksum over the provided number. The checksum
     is returned as an int. Valid numbers should have a checksum of 0."""
     n = len(alphabet)
-    number = tuple(alphabet.index(i)
+    values = tuple(alphabet.index(i)
                    for i in reversed(str(number)))
-    return (sum(number[::2]) +
+    return (sum(values[::2]) +
             sum(sum(divmod(i * 2, n))
-                for i in number[1::2])) % n
+                for i in values[1::2])) % n
 
 
-def validate(number, alphabet='0123456789'):
+def validate(number: str, alphabet: str = '0123456789') -> str:
     """Check if the number provided passes the Luhn checksum."""
     if not bool(number):
         raise InvalidFormat()
@@ -71,7 +73,7 @@ def validate(number, alphabet='0123456789'):
     return number
 
 
-def is_valid(number, alphabet='0123456789'):
+def is_valid(number: str, alphabet: str = '0123456789') -> bool:
     """Check if the number passes the Luhn checksum."""
     try:
         return bool(validate(number, alphabet))
@@ -79,7 +81,7 @@ def is_valid(number, alphabet='0123456789'):
         return False
 
 
-def calc_check_digit(number, alphabet='0123456789'):
+def calc_check_digit(number: str, alphabet: str = '0123456789') -> str:
     """Calculate the extra digit that should be appended to the number to
     make it a valid number."""
     ck = checksum(str(number) + alphabet[0], alphabet)
diff --git a/stdnum/lv/__init__.py b/stdnum/lv/__init__.py
index e6de359..3cfc1a1 100644
--- a/stdnum/lv/__init__.py
+++ b/stdnum/lv/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Latvian numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.lv import pvn as vat  # noqa: F401
diff --git a/stdnum/lv/pvn.py b/stdnum/lv/pvn.py
index 006b5c0..6a680c8 100644
--- a/stdnum/lv/pvn.py
+++ b/stdnum/lv/pvn.py
@@ -39,6 +39,8 @@ Traceback (most recent call last):
 InvalidComponent: ...
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum.exceptions import *
@@ -50,7 +52,7 @@ from stdnum.util import clean, isdigits
 # https://www6.vid.gov.lv/VID_PDB?aspxerrorpath=/vid_pdb/pvn.asp
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').upper().strip()
@@ -59,13 +61,13 @@ def compact(number):
     return number
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum for legal entities."""
     weights = (9, 1, 4, 8, 3, 10, 2, 5, 7, 6, 1)
     return sum(w * int(n) for w, n in zip(weights, number)) % 11
 
 
-def calc_check_digit_pers(number):
+def calc_check_digit_pers(number: str) -> str:
     """Calculate the check digit for personal codes. The number passed
     should not have the check digit included."""
     # note that this algorithm has not been confirmed by an independent source
@@ -74,7 +76,7 @@ def calc_check_digit_pers(number):
     return str(check % 11 % 10)
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Split the date parts from the number and return the birth date."""
     number = compact(number)
     day = int(number[0:2])
@@ -87,7 +89,7 @@ def get_birth_date(number):
         raise InvalidComponent()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -108,7 +110,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/ma/__init__.py b/stdnum/ma/__init__.py
index d2b0ff2..d7e0269 100644
--- a/stdnum/ma/__init__.py
+++ b/stdnum/ma/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Moroccan numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.ma import ice as vat  # noqa: F401
diff --git a/stdnum/ma/ice.py b/stdnum/ma/ice.py
index 10d4023..73267c5 100644
--- a/stdnum/ma/ice.py
+++ b/stdnum/ma/ice.py
@@ -60,12 +60,14 @@ InvalidChecksum: ...
 '002136093000040'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_97_10
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators, removes surrounding
@@ -74,7 +76,7 @@ def compact(number):
     return clean(number, ' ')
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Morocco ICE number.
 
     This checks the length and formatting.
@@ -89,7 +91,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Morocco ICE number."""
     try:
         return bool(validate(number))
@@ -97,6 +99,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number).zfill(15)
diff --git a/stdnum/mac.py b/stdnum/mac.py
index 947e893..93a3f81 100644
--- a/stdnum/mac.py
+++ b/stdnum/mac.py
@@ -43,6 +43,8 @@ False
 '84A2A0'
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum import numdb
@@ -53,14 +55,14 @@ from stdnum.util import clean
 _mac_re = re.compile('^([0-9a-f]{2}:){5}[0-9a-f]{2}$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the MAC address to the minimal, consistent representation."""
     number = clean(number, ' ').strip().lower().replace('-', ':')
     # zero-pad single-digit elements
     return ':'.join('0' + n if len(n) == 1 else n for n in number.split(':'))
 
 
-def _lookup(number):
+def _lookup(number: str) -> tuple[str, str]:
     """Look up the manufacturer in the IEEE OUI registry."""
     number = compact(number).replace(':', '').upper()
     info = numdb.get('oui').info(number)
@@ -72,23 +74,23 @@ def _lookup(number):
         raise InvalidComponent()
 
 
-def get_manufacturer(number):
+def get_manufacturer(number: str) -> str:
     """Look up the manufacturer in the IEEE OUI registry."""
     return _lookup(number)[1]
 
 
-def get_oui(number):
+def get_oui(number: str) -> str:
     """Return the OUI (organization unique ID) part of the address."""
     return _lookup(number)[0]
 
 
-def get_iab(number):
+def get_iab(number: str) -> str:
     """Return the IAB (individual address block) part of the address."""
     number = compact(number).replace(':', '').upper()
     return number[len(get_oui(number)):]
 
 
-def is_unicast(number):
+def is_unicast(number: str) -> bool:
     """Check whether the number is a unicast address.
 
     Unicast addresses are received by one node in a network (LAN)."""
@@ -96,7 +98,7 @@ def is_unicast(number):
     return int(number[:2], 16) & 1 == 0
 
 
-def is_multicast(number):
+def is_multicast(number: str) -> bool:
     """Check whether the number is a multicast address.
 
     Multicast addresses are meant to be received by (potentially) multiple
@@ -104,7 +106,7 @@ def is_multicast(number):
     return not is_unicast(number)
 
 
-def is_broadcast(number):
+def is_broadcast(number: str) -> bool:
     """Check whether the number is the broadcast address.
 
     Broadcast addresses are meant to be received by all nodes in a network."""
@@ -112,18 +114,18 @@ def is_broadcast(number):
     return number == 'ff:ff:ff:ff:ff:ff'
 
 
-def is_universally_administered(number):
+def is_universally_administered(number: str) -> bool:
     """Check if the address is supposed to be assigned by the manufacturer."""
     number = compact(number)
     return int(number[:2], 16) & 2 == 0
 
 
-def is_locally_administered(number):
+def is_locally_administered(number: str) -> bool:
     """Check if the address is meant to be configured by an administrator."""
     return not is_universally_administered(number)
 
 
-def validate(number, validate_manufacturer=None):
+def validate(number: str, validate_manufacturer: bool | None = None) -> str:
     """Check if the number provided is a valid MAC address.
 
     The existence of the manufacturer is by default only checked for
@@ -141,7 +143,7 @@ def validate(number, validate_manufacturer=None):
     return number
 
 
-def is_valid(number, validate_manufacturer=None):
+def is_valid(number: str, validate_manufacturer: bool | None = None) -> bool:
     """Check if the number provided is a valid IBAN."""
     try:
         return bool(validate(number, 
validate_manufacturer=validate_manufacturer))
@@ -149,6 +151,6 @@ def is_valid(number, validate_manufacturer=None):
         return False
 
 
-def to_eui48(number):
+def to_eui48(number: str) -> str:
     """Convert the MAC address to EUI-48 format."""
     return compact(number).upper().replace(':', '-')
diff --git a/stdnum/mc/__init__.py b/stdnum/mc/__init__.py
index 33e9a33..dc9717b 100644
--- a/stdnum/mc/__init__.py
+++ b/stdnum/mc/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Monacan numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.mc import tva as vat  # noqa: F401
diff --git a/stdnum/mc/tva.py b/stdnum/mc/tva.py
index 0fe3bf5..c709b5d 100644
--- a/stdnum/mc/tva.py
+++ b/stdnum/mc/tva.py
@@ -34,17 +34,19 @@ Traceback (most recent call last):
 InvalidComponent: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.fr import tva
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return 'FR' + tva.compact(number)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = tva.validate(number)
@@ -53,7 +55,7 @@ def validate(number):
     return 'FR' + number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/md/idno.py b/stdnum/md/idno.py
index 5673d7f..44172d4 100644
--- a/stdnum/md/idno.py
+++ b/stdnum/md/idno.py
@@ -37,23 +37,25 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     weights = (7, 3, 1, 7, 3, 1, 7, 3, 1, 7, 3, 1)
     return str(sum(w * int(n) for w, n in zip(weights, number)) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is valid. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -66,7 +68,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is valid. This checks the length,
     formatting and check digit."""
     try:
diff --git a/stdnum/me/__init__.py b/stdnum/me/__init__.py
index 5040786..5142dfe 100644
--- a/stdnum/me/__init__.py
+++ b/stdnum/me/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Montenegro numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.me import pib as vat  # noqa: F401
diff --git a/stdnum/me/iban.py b/stdnum/me/iban.py
index 248aa22..17bf8a2 100644
--- a/stdnum/me/iban.py
+++ b/stdnum/me/iban.py
@@ -37,6 +37,8 @@ Traceback (most recent call last):
 InvalidComponent: ...
 """
 
+from __future__ import annotations
+
 from stdnum import iban
 from stdnum.exceptions import *
 
@@ -48,12 +50,12 @@ compact = iban.compact
 format = iban.format
 
 
-def _checksum(number):
+def _checksum(number: str) -> int:
     """Calculate the check digits over the provided part of the number."""
     return int(number) % 97
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid Montenegro IBAN."""
     number = iban.validate(number, check_country=False)
     if not number.startswith('ME'):
@@ -63,7 +65,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid Montenegro IBAN."""
     try:
         return bool(validate(number))
diff --git a/stdnum/me/pib.py b/stdnum/me/pib.py
index 22216c3..48d2361 100644
--- a/stdnum/me/pib.py
+++ b/stdnum/me/pib.py
@@ -38,11 +38,13 @@ InvalidChecksum: ...
 '02655284'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -51,13 +53,13 @@ def compact(number):
     return clean(number, ' ')
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for the number."""
     weights = (8, 7, 6, 5, 4, 3, 2)
     return str((-sum(w * int(n) for w, n in zip(weights, number))) % 11 % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Montenegro PIB number."""
     number = compact(number)
     if len(number) != 8:
@@ -69,7 +71,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Montenegro PIB number."""
     try:
         return bool(validate(number))
@@ -77,6 +79,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/meid.py b/stdnum/meid.py
index d883eec..555898d 100644
--- a/stdnum/meid.py
+++ b/stdnum/meid.py
@@ -38,6 +38,8 @@ InvalidChecksum: ...
 '29360 87365 0070 3710 0'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
@@ -45,20 +47,20 @@ from stdnum.util import clean, isdigits
 _hex_alphabet = '0123456789ABCDEF'
 
 
-def _cleanup(number):
+def _cleanup(number: str) -> str:
     """Remove any grouping information from the number and removes surrounding
     whitespace."""
     return clean(number, ' -').strip().upper()
 
 
-def _ishex(number):
+def _ishex(number: str) -> bool:
     for x in number:
         if x not in _hex_alphabet:
             return False
     return True
 
 
-def _parse(number):
+def _parse(number: str) -> tuple[str, str]:
     number = _cleanup(number)
     if len(number) in (14, 15):
         # 14 or 15 digit hex representation
@@ -74,7 +76,7 @@ def _parse(number):
         raise InvalidLength()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for the number. The number should not
     already have a check digit."""
     # both the 18-digit decimal format and the 14-digit hex format
@@ -86,7 +88,7 @@ def calc_check_digit(number):
         return luhn.calc_check_digit(number, alphabet=_hex_alphabet)
 
 
-def compact(number, strip_check_digit=True):
+def compact(number: str, strip_check_digit: bool = True) -> str:
     """Convert the MEID number to the minimal (hexadecimal) representation.
     This strips grouping information, removes surrounding whitespace and
     converts to hexadecimal if needed. If the check digit is to be preserved
@@ -105,7 +107,7 @@ def compact(number, strip_check_digit=True):
     return number + cd
 
 
-def validate(number, strip_check_digit=True):
+def validate(number: str, strip_check_digit: bool = True) -> str:
     """Check if the number is a valid MEID number. This converts the
     representation format of the number (if it is decimal it is not converted
     to hexadecimal)."""
@@ -136,7 +138,7 @@ def validate(number, strip_check_digit=True):
     return number + cd
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid MEID number."""
     try:
         return bool(validate(number))
@@ -144,7 +146,12 @@ def is_valid(number):
         return False
 
 
-def format(number, separator=' ', format=None, add_check_digit=False):
+def format(
+    number: str,
+    separator: str = ' ',
+    format: str | None = None,
+    add_check_digit: bool = False,
+) -> str:
     """Reformat the number to the standard presentation format. The separator
     used can be provided. If the format is specified (either 'hex' or 'dec')
     the number is reformatted in that format, otherwise the current
@@ -168,21 +175,21 @@ def format(number, separator=' ', format=None, 
add_check_digit=False):
         cd = calc_check_digit(number)
     # split number according to format
     if len(number) == 14:
-        number = [number[i * 2:i * 2 + 2]
-                  for i in range(7)] + [cd]
+        parts = [number[i * 2:i * 2 + 2]
+                 for i in range(7)] + [cd]
     else:
-        number = (number[:5], number[5:10], number[10:14], number[14:], cd)
-    return separator.join(x for x in number if x)
+        parts = [number[:5], number[5:10], number[10:14], number[14:], cd]
+    return separator.join(x for x in parts if x)
 
 
-def to_binary(number):
+def to_binary(number: str) -> bytes:
     """Convert the number to its binary representation (without the check
     digit)."""
     from binascii import a2b_hex
     return a2b_hex(compact(number, strip_check_digit=True))
 
 
-def to_pseudo_esn(number):
+def to_pseudo_esn(number: str) -> str:
     """Convert the provided MEID to a pseudo ESN (pESN). The ESN is returned
     in compact hexadecimal representation."""
     # return the last 6 digits of the SHA1  hash prefixed with the reserved
diff --git a/stdnum/mk/__init__.py b/stdnum/mk/__init__.py
index fac842e..460926e 100644
--- a/stdnum/mk/__init__.py
+++ b/stdnum/mk/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of North Macedonia numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.mk import edb as vat  # noqa: F401
diff --git a/stdnum/mk/edb.py b/stdnum/mk/edb.py
index c654dbd..7ff7710 100644
--- a/stdnum/mk/edb.py
+++ b/stdnum/mk/edb.py
@@ -43,11 +43,13 @@ InvalidChecksum: ...
 '4057009501106'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -61,14 +63,14 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     weights = (7, 6, 5, 4, 3, 2, 7, 6, 5, 4, 3, 2)
     total = sum(int(n) * w for n, w in zip(number, weights))
     return str((-total % 11) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid North Macedonia ЕДБ number.
 
     This checks the length, formatting and check digit.
@@ -83,7 +85,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid North Macedonia ЕДБ number."""
     try:
         return bool(validate(number))
@@ -91,6 +93,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/mt/vat.py b/stdnum/mt/vat.py
index 6d7899e..acfb85a 100644
--- a/stdnum/mt/vat.py
+++ b/stdnum/mt/vat.py
@@ -30,11 +30,13 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').upper().strip()
@@ -43,13 +45,13 @@ def compact(number):
     return number
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum."""
     weights = (3, 4, 6, 7, 8, 9, 10, 1)
     return sum(w * int(n) for w, n in zip(weights, number)) % 37
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -62,7 +64,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/mu/nid.py b/stdnum/mu/nid.py
index fef4193..e6a93c8 100644
--- a/stdnum/mu/nid.py
+++ b/stdnum/mu/nid.py
@@ -38,6 +38,8 @@ More information:
 * https://mnis.govmu.org/English/ID%20Card/Pages/default.aspx
 """
 
+from __future__ import annotations
+
 import datetime
 import re
 
@@ -52,20 +54,20 @@ _nid_re = re.compile('^[A-Z][0-9]+[0-9A-Z]$')
 _alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips
     surrounding whitespace and separation dash."""
     return clean(number, ' ').upper().strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for the number."""
     check = sum((14 - i) * _alphabet.index(n)
                 for i, n in enumerate(number[:13]))
     return _alphabet[(17 - check) % 17]
 
 
-def _get_date(number):
+def _get_date(number: str) -> datetime.date:
     """Convert the part of the number that represents a date into a
     datetime. Note that the century may be incorrect."""
     day = int(number[1:3])
@@ -77,7 +79,7 @@ def _get_date(number):
         raise InvalidComponent()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid ID number."""
     number = compact(number)
     if len(number) != 14:
@@ -90,7 +92,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid RFC."""
     try:
         return bool(validate(number))
diff --git a/stdnum/mx/__init__.py b/stdnum/mx/__init__.py
index 51c4fe4..44c78e7 100644
--- a/stdnum/mx/__init__.py
+++ b/stdnum/mx/__init__.py
@@ -20,6 +20,8 @@
 
 """Collection of Mexican numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.mx import curp as personalid  # noqa: F401
 from stdnum.mx import rfc as vat  # noqa: F401
diff --git a/stdnum/mx/curp.py b/stdnum/mx/curp.py
index 33b9168..de5ca87 100644
--- a/stdnum/mx/curp.py
+++ b/stdnum/mx/curp.py
@@ -42,6 +42,8 @@ datetime.date(1931, 8, 20)
 'M'
 """
 
+from __future__ import annotations
+
 import datetime
 import re
 
@@ -66,13 +68,13 @@ _valid_states = set('''
 '''.split())
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips
     surrounding whitespace and separation dash."""
     return clean(number, '-_ ').upper().strip()
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Split the date parts from the number and return the birth date."""
     number = compact(number)
     year = int(number[4:6])
@@ -88,7 +90,7 @@ def get_birth_date(number):
         raise InvalidComponent()
 
 
-def get_gender(number):
+def get_gender(number: str) -> str:
     """Get the gender (M/F) from the person's CURP."""
     number = compact(number)
     if number[10] == 'H':
@@ -103,13 +105,13 @@ def get_gender(number):
 _alphabet = '0123456789ABCDEFGHIJKLMN&OPQRSTUVWXYZ'
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     check = sum(_alphabet.index(c) * (18 - i) for i, c in 
enumerate(number[:17]))
     return str((10 - check % 10) % 10)
 
 
-def validate(number, validate_check_digits=True):
+def validate(number: str, validate_check_digits: bool = True) -> str:
     """Check if the number is a valid CURP."""
     number = compact(number)
     if len(number) != 18:
@@ -127,7 +129,7 @@ def validate(number, validate_check_digits=True):
     return number
 
 
-def is_valid(number, validate_check_digits=True):
+def is_valid(number: str, validate_check_digits: bool = True) -> bool:
     """Check if the number provided is a valid CURP."""
     try:
         return bool(validate(number, validate_check_digits))
diff --git a/stdnum/mx/rfc.py b/stdnum/mx/rfc.py
index 5bb6cf0..d2494b3 100644
--- a/stdnum/mx/rfc.py
+++ b/stdnum/mx/rfc.py
@@ -60,6 +60,8 @@ InvalidChecksum: ...
 'GODE 561231 GR8'
 """
 
+from __future__ import annotations
+
 import datetime
 import re
 
@@ -81,13 +83,13 @@ _name_blacklist = set([
 _alphabet = '0123456789ABCDEFGHIJKLMN&OPQRSTUVWXYZ Ñ'
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips
     surrounding whitespace and separation dash."""
     return clean(number, '-_ ').upper().strip()
 
 
-def _get_date(number):
+def _get_date(number: str) -> datetime.date:
     """Convert the part of the number that represents a date into a
     datetime. Note that the century may be incorrect."""
     year = int(number[0:2])
@@ -99,7 +101,7 @@ def _get_date(number):
         raise InvalidComponent()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     number = ('   ' + number)[-12:]
@@ -107,7 +109,7 @@ def calc_check_digit(number):
     return _alphabet[(11 - check) % 11]
 
 
-def validate(number, validate_check_digits=False):
+def validate(number: str, validate_check_digits: bool = False) -> str:
     """Check if the number is a valid RFC."""
     number = compact(number)
     if len(number) in (10, 13):
@@ -132,7 +134,7 @@ def validate(number, validate_check_digits=False):
     return number
 
 
-def is_valid(number, validate_check_digits=False):
+def is_valid(number: str, validate_check_digits: bool = False) -> bool:
     """Check if the number provided is a valid RFC."""
     try:
         return bool(validate(number, validate_check_digits))
@@ -140,7 +142,7 @@ def is_valid(number, validate_check_digits=False):
         return False
 
 
-def format(number, separator=' '):
+def format(number: str, separator: str = ' ') -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     if len(number) == 12:
diff --git a/stdnum/my/nric.py b/stdnum/my/nric.py
index c3283d7..7c85ad4 100644
--- a/stdnum/my/nric.py
+++ b/stdnum/my/nric.py
@@ -41,19 +41,21 @@ InvalidComponent: ...
 '770305-02-1234'
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -*').strip()
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Split the date parts from the number and return the birth date.
     Note that in some cases it may return the registration date instead of
     the birth date and it may be a century off."""
@@ -72,7 +74,7 @@ def get_birth_date(number):
         raise InvalidComponent()
 
 
-def get_birth_place(number):
+def get_birth_place(number: str) -> dict[str, str]:
     """Use the number to look up the place of birth of the person. This can
     either be a state or federal territory within Malaysia or a country
     outside of Malaysia."""
@@ -84,7 +86,7 @@ def get_birth_place(number):
     return results
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid NRIC number. This checks the length,
     formatting and birth date and place."""
     number = compact(number)
@@ -97,7 +99,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid NRIC number."""
     try:
         return bool(validate(number))
@@ -105,7 +107,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return number[:6] + '-' + number[6:8] + '-' + number[8:]
diff --git a/stdnum/nl/__init__.py b/stdnum/nl/__init__.py
index 408d065..0523a37 100644
--- a/stdnum/nl/__init__.py
+++ b/stdnum/nl/__init__.py
@@ -20,6 +20,8 @@
 
 """Collection of Dutch numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.nl import btw as vat  # noqa: F401
 from stdnum.nl import postcode as postal_code  # noqa: F401
diff --git a/stdnum/nl/brin.py b/stdnum/nl/brin.py
index 656873a..c3dcc82 100644
--- a/stdnum/nl/brin.py
+++ b/stdnum/nl/brin.py
@@ -45,6 +45,8 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -57,13 +59,13 @@ from stdnum.util import clean
 _brin_re = re.compile(r'^(?P<brin>[0-9]{2}[A-Z]{2})(?P<location>[0-9]{2})?$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -.').upper().strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Brun number. This currently does not
     check whether the number points to a registered school."""
     number = compact(number)
@@ -75,7 +77,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Brun number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/nl/bsn.py b/stdnum/nl/bsn.py
index 41047e4..cb8dca5 100644
--- a/stdnum/nl/bsn.py
+++ b/stdnum/nl/bsn.py
@@ -44,24 +44,26 @@ InvalidLength: ...
 '1112.22.333'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -.').strip().zfill(9)
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum over the number. A valid number should have
     a checksum of 0."""
     return (sum((9 - i) * int(n) for i, n in enumerate(number[:-1])) -
             int(number[-1])) % 11
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid BSN. This checks the length and whether
     the check digit is correct."""
     number = compact(number)
@@ -74,7 +76,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid BSN."""
     try:
         return bool(validate(number))
@@ -82,7 +84,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the passed number to the standard presentation format."""
     number = compact(number)
     return number[:4] + '.' + number[4:6] + '.' + number[6:]
diff --git a/stdnum/nl/btw.py b/stdnum/nl/btw.py
index 6d12372..08a97b0 100644
--- a/stdnum/nl/btw.py
+++ b/stdnum/nl/btw.py
@@ -47,13 +47,15 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_97_10
 from stdnum.nl import bsn
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -.').upper().strip()
@@ -62,7 +64,7 @@ def compact(number):
     return bsn.compact(number[:-3]) + number[-3:]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid btw number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -79,7 +81,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid btw number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/nl/identiteitskaartnummer.py 
b/stdnum/nl/identiteitskaartnummer.py
index 4bde229..2431b5f 100644
--- a/stdnum/nl/identiteitskaartnummer.py
+++ b/stdnum/nl/identiteitskaartnummer.py
@@ -54,19 +54,21 @@ Traceback (most recent call last):
 InvalidComponent: ...
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
 from stdnum.util import clean
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding white space."""
     return clean(number, ' ').strip().upper()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid passport number.
     This checks the length, formatting and check digit."""
     number = compact(number)
@@ -79,7 +81,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Dutch passport number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/nl/onderwijsnummer.py b/stdnum/nl/onderwijsnummer.py
index ad3545c..0fa6a30 100644
--- a/stdnum/nl/onderwijsnummer.py
+++ b/stdnum/nl/onderwijsnummer.py
@@ -43,6 +43,8 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.nl.bsn import checksum, compact
 from stdnum.util import isdigits
@@ -51,7 +53,7 @@ from stdnum.util import isdigits
 __all__ = ['compact', 'validate', 'is_valid']
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid onderwijsnummer. This checks the length
     and whether the check digit is correct and whether it starts with the
     right sequence."""
@@ -67,7 +69,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid onderwijsnummer."""
     try:
         return bool(validate(number))
diff --git a/stdnum/nl/postcode.py b/stdnum/nl/postcode.py
index e91acc8..2f3ba97 100644
--- a/stdnum/nl/postcode.py
+++ b/stdnum/nl/postcode.py
@@ -40,6 +40,8 @@ Traceback (most recent call last):
 InvalidComponent: ...
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -52,7 +54,7 @@ _postcode_re = 
re.compile(r'^(?P<pt1>[1-9][0-9]{3})(?P<pt2>[A-Z]{2})$')
 _postcode_blacklist = ('SA', 'SD', 'SS')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').upper().strip()
@@ -61,7 +63,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is in the correct format. This currently does not
     check whether the code corresponds to a real address."""
     number = compact(number)
@@ -73,7 +75,7 @@ def validate(number):
     return '%s %s' % (match.group('pt1'), match.group('pt2'))
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid postal code."""
     try:
         return bool(validate(number))
diff --git a/stdnum/no/__init__.py b/stdnum/no/__init__.py
index 2a9e88d..9dfe517 100644
--- a/stdnum/no/__init__.py
+++ b/stdnum/no/__init__.py
@@ -20,6 +20,8 @@
 
 """Collection of Norwegian numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.no import fodselsnummer as personalid  # noqa: F401
 from stdnum.no import mva as vat  # noqa: F401
diff --git a/stdnum/no/fodselsnummer.py b/stdnum/no/fodselsnummer.py
index ccdf1ad..f070685 100644
--- a/stdnum/no/fodselsnummer.py
+++ b/stdnum/no/fodselsnummer.py
@@ -42,31 +42,33 @@ InvalidChecksum: ...
 '151086 95077'
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -:')
 
 
-def calc_check_digit1(number):
+def calc_check_digit1(number: str) -> str:
     """Calculate the first check digit for the number."""
     weights = (3, 7, 6, 1, 8, 9, 4, 5, 2)
     return str((11 - sum(w * int(n) for w, n in zip(weights, number))) % 11)
 
 
-def calc_check_digit2(number):
+def calc_check_digit2(number: str) -> str:
     """Calculate the second check digit for the number."""
     weights = (5, 4, 3, 2, 7, 6, 5, 4, 3, 2)
     return str((11 - sum(w * int(n) for w, n in zip(weights, number))) % 11)
 
 
-def get_gender(number):
+def get_gender(number: str) -> str:
     """Get the person's birth gender ('M' or 'F')."""
     number = compact(number)
     if int(number[8]) % 2:
@@ -75,7 +77,7 @@ def get_gender(number):
         return 'F'
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Determine and return the birth date."""
     number = compact(number)
     day = int(number[0:2])
@@ -111,7 +113,7 @@ def get_birth_date(number):
         raise InvalidComponent('The number does not contain valid birth date 
information.')
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid birth number."""
     number = compact(number)
     if len(number) != 11:
@@ -128,7 +130,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid birth number."""
     try:
         return bool(validate(number))
@@ -136,7 +138,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return number[:6] + ' ' + number[6:]
diff --git a/stdnum/no/iban.py b/stdnum/no/iban.py
index 34a0ac7..1987277 100644
--- a/stdnum/no/iban.py
+++ b/stdnum/no/iban.py
@@ -44,6 +44,8 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum import iban
 from stdnum.exceptions import *
 from stdnum.no import kontonr
@@ -56,7 +58,7 @@ compact = iban.compact
 format = iban.format
 
 
-def to_kontonr(number):
+def to_kontonr(number: str) -> str:
     """Return the Norwegian bank account number part of the number."""
     number = compact(number)
     if not number.startswith('NO'):
@@ -64,14 +66,14 @@ def to_kontonr(number):
     return number[4:]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid Norwegian IBAN."""
     number = iban.validate(number, check_country=False)
     kontonr.validate(to_kontonr(number))
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid Norwegian IBAN."""
     try:
         return bool(validate(number))
diff --git a/stdnum/no/kontonr.py b/stdnum/no/kontonr.py
index addc767..aa233c4 100644
--- a/stdnum/no/kontonr.py
+++ b/stdnum/no/kontonr.py
@@ -42,12 +42,14 @@ InvalidChecksum: ...
 'NO93 8601 11 17947'
 """
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' .-').strip()
@@ -56,13 +58,13 @@ def compact(number):
     return number
 
 
-def _calc_check_digit(number):
+def _calc_check_digit(number: str) -> str:
     """Calculate the check digit for the 11-digit number."""
     weights = (6, 7, 8, 9, 4, 5, 6, 7, 8, 9)
     return str(sum(w * int(n) for w, n in zip(weights, number)) % 11)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid bank account number."""
     number = compact(number)
     if not isdigits(number):
@@ -77,7 +79,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid bank account number."""
     try:
         return bool(validate(number))
@@ -85,7 +87,7 @@ def is_valid(number):
         return False
 
 
-def to_iban(number):
+def to_iban(number: str) -> str:
     """Convert the number to an IBAN."""
     from stdnum import iban
     separator = ' ' if ' ' in number else ''
@@ -94,7 +96,7 @@ def to_iban(number):
         number))
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number).zfill(11)
     return '.'.join([
diff --git a/stdnum/no/mva.py b/stdnum/no/mva.py
index 60a1421..4409a54 100644
--- a/stdnum/no/mva.py
+++ b/stdnum/no/mva.py
@@ -34,12 +34,14 @@ InvalidChecksum: ...
 'NO 995 525 828 MVA'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.no import orgnr
 from stdnum.util import clean
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' ').upper().strip()
@@ -48,7 +50,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid MVA number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -58,7 +60,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid MVA number."""
     try:
         return bool(validate(number))
@@ -66,7 +68,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return 'NO ' + orgnr.format(number[:9]) + ' ' + number[9:]
diff --git a/stdnum/no/orgnr.py b/stdnum/no/orgnr.py
index d1bcf65..884fc9b 100644
--- a/stdnum/no/orgnr.py
+++ b/stdnum/no/orgnr.py
@@ -40,23 +40,25 @@ InvalidChecksum: ...
 '988 077 917'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip()
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum."""
     weights = (3, 2, 7, 6, 5, 4, 3, 2, 1)
     return sum(w * int(n) for w, n in zip(weights, number)) % 11
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid organisation number. This checks the
     length, formatting and check digit."""
     number = compact(number)
@@ -69,7 +71,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid organisation number."""
     try:
         return bool(validate(number))
@@ -77,7 +79,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return number[:3] + ' ' + number[3:6] + ' ' + number[6:]
diff --git a/stdnum/numdb.py b/stdnum/numdb.py
index c9c7283..489249f 100644
--- a/stdnum/numdb.py
+++ b/stdnum/numdb.py
@@ -59,9 +59,18 @@ To split the number and get properties for each part:
 
 """
 
+from __future__ import annotations
+
 import re
 
 
+TYPE_CHECKING = False
+if TYPE_CHECKING:  # pragma: no cover (only used when type checking)
+    from collections.abc import Generator, Iterable
+    from typing import IO, Any
+    PrefixInfo = tuple[int, str, str, dict[str, str], list['PrefixInfo']]
+
+
 _line_re = re.compile(
     r'^(?P<indent> *)'
     r'(?P<ranges>([^-,\s]+(-[^-,\s]+)?)(,[^-,\s]+(-[^-,\s]+)?)*)\s*'
@@ -84,19 +93,21 @@ _open_databases = {}
 class NumDB():
     """Number database."""
 
-    def __init__(self):
+    prefixes: list[PrefixInfo]
+
+    def __init__(self) -> None:
         """Construct an empty database."""
         self.prefixes = []
 
     @staticmethod
-    def _find(number, prefixes):
+    def _find(number: str, prefixes: list[PrefixInfo]) -> list[tuple[str, 
dict[str, str]]]:
         """Lookup the specified number in the list of prefixes, this will
         return basically what info() should return but works recursively."""
         if not number:
             return []
         part = number
-        properties = {}
-        next_prefixes = []
+        properties: dict[str, Any] = {}
+        next_prefixes: list[PrefixInfo] = []
         # go over prefixes and find matches
         for length, low, high, props, children in prefixes:
             if len(part) >= length and low <= part[:length] <= high:
@@ -110,20 +121,22 @@ class NumDB():
         # return first part and recursively find next matches
         return [(part, properties)] + NumDB._find(number[len(part):], 
next_prefixes)
 
-    def info(self, number):
+    def info(self, number: str) -> list[tuple[str, dict[str, str]]]:
         """Split the provided number in components and associate properties
         with each component. This returns a tuple of tuples. Each tuple
         consists of a string (a part of the number) and a dict of properties.
         """
         return NumDB._find(number, self.prefixes)
 
-    def split(self, number):
+    def split(self, number: str) -> list[str]:
         """Split the provided number in components. This returns a tuple with
         the number of components identified."""
         return [part for part, props in self.info(number)]
 
 
-def _parse(fp):
+def _parse(
+    fp: Iterable[str],
+) -> Generator[tuple[int, int, str, str, dict[str, str], list[PrefixInfo]]]:
     """Read lines of text from the file pointer and generate indent, length,
     low, high, properties tuples."""
     for line in fp:
@@ -132,10 +145,11 @@ def _parse(fp):
             continue  # pragma: no cover (optimisation takes it out)
         # any other line should parse
         match = _line_re.search(line)
+        assert match is not None
         indent = len(match.group('indent'))
         ranges = match.group('ranges')
         props = dict(_prop_re.findall(match.group('props')))
-        children = []
+        children: list[PrefixInfo] = []
         for rnge in ranges.split(','):
             if '-' in rnge:
                 low, high = rnge.split('-')
@@ -144,7 +158,7 @@ def _parse(fp):
             yield indent, len(low), low, high, props, children
 
 
-def read(fp):
+def read(fp: Iterable[str]) -> NumDB:
     """Return a new database with the data read from the specified file."""
     last_indent = 0
     db = NumDB()
@@ -153,22 +167,22 @@ def read(fp):
         if indent > last_indent:
             # set our stack location to the last parent entry
             stack[indent] = stack[last_indent][-1][4]
-        stack[indent].append([length, low, high, props, children])
+        stack[indent].append((length, low, high, props, children))
         last_indent = indent
     return db
 
 
-def _get_resource_stream(name):
+def _get_resource_stream(name: str) -> IO[bytes]:
     """Return a readable file-like object for the resource."""
     try:  # pragma: no cover (Python 3.9 and newer)
         import importlib.resources
         return importlib.resources.files(__package__).joinpath(name).open('rb')
     except (ImportError, AttributeError):  # pragma: no cover (older Python 
versions)
-        import pkg_resources
-        return pkg_resources.resource_stream(__name__, name)
+        import pkg_resources  # type: ignore[import-untyped]
+        return pkg_resources.resource_stream(__name__, name)  # type: 
ignore[no-any-return]
 
 
-def get(name):
+def get(name: str) -> NumDB:
     """Open a database with the specified name to perform queries on."""
     if name not in _open_databases:
         import codecs
diff --git a/stdnum/nz/__init__.py b/stdnum/nz/__init__.py
index 150057e..916154b 100644
--- a/stdnum/nz/__init__.py
+++ b/stdnum/nz/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of New Zealand numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.nz import ird as vat  # noqa: F401
diff --git a/stdnum/nz/bankaccount.py b/stdnum/nz/bankaccount.py
index 3a56178..837f530 100644
--- a/stdnum/nz/bankaccount.py
+++ b/stdnum/nz/bankaccount.py
@@ -42,6 +42,8 @@ InvalidComponent: ...
 '01-0242-0100194-000'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
@@ -84,21 +86,21 @@ _moduli = {
 }
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
-    number = clean(number).strip().replace(' ', '-').split('-')
-    if len(number) == 4:
+    parts = clean(number).strip().replace(' ', '-').split('-')
+    if len(parts) == 4:
         # zero pad the different sections if they are found
         lengths = (2, 4, 7, 3)
-        return ''.join(n.zfill(l) for n, l in zip(number, lengths))
+        return ''.join(n.zfill(l) for n, l in zip(parts, lengths))
     else:
         # otherwise zero pad the account type
-        number = ''.join(number)
+        number = ''.join(parts)
         return number[:13] + number[13:].zfill(3)
 
 
-def info(number):
+def info(number: str) -> dict[str, str]:
     """Return a dictionary of data about the supplied number. This typically
     returns the name of the bank and branch and a BIC if it is valid."""
     number = compact(number)
@@ -109,7 +111,7 @@ def info(number):
     return info
 
 
-def _calc_checksum(number):
+def _calc_checksum(number: str) -> int:
     # pick the algorithm and parameters
     algorithm = _algorithms.get(number[:2], 'X')
     if algorithm == 'A' and number[6:13] >= '0990000':
@@ -122,7 +124,7 @@ def _calc_checksum(number):
         (w * int(n) for w, n in zip(weights, number))) % mod2
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid bank account number."""
     number = compact(number)
     if not isdigits(number):
@@ -137,7 +139,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid bank account number."""
     try:
         return bool(validate(number))
@@ -145,7 +147,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join([
diff --git a/stdnum/nz/ird.py b/stdnum/nz/ird.py
index 6bd313b..c0e8868 100644
--- a/stdnum/nz/ird.py
+++ b/stdnum/nz/ird.py
@@ -46,11 +46,13 @@ InvalidLength: ...
 '49-098-576'
 """  # noqa: E501
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation."""
     number = clean(number, ' -').upper().strip()
     if number.startswith('NZ'):
@@ -58,7 +60,7 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit.
 
     The number passed should not have the check digit included.
@@ -74,7 +76,7 @@ def calc_check_digit(number):
     return str(s)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid IRD number."""
     number = compact(number)
     if len(number) not in (8, 9):
@@ -88,7 +90,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid IRD number."""
     try:
         return bool(validate(number))
@@ -96,7 +98,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join([number[:-6], number[-6:-3], number[-3:]])
diff --git a/stdnum/pe/__init__.py b/stdnum/pe/__init__.py
index 80bbd52..34e38ec 100644
--- a/stdnum/pe/__init__.py
+++ b/stdnum/pe/__init__.py
@@ -19,4 +19,7 @@
 # 02110-1301 USA
 
 """Collection of Peruvian numbers."""
+
+from __future__ import annotations
+
 from stdnum.pe import ruc as vat  # noqa: F401
diff --git a/stdnum/pe/cui.py b/stdnum/pe/cui.py
index d9c37ea..0224fbc 100644
--- a/stdnum/pe/cui.py
+++ b/stdnum/pe/cui.py
@@ -42,17 +42,19 @@ InvalidChecksum: ...
 '10101174102'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip().upper()
 
 
-def calc_check_digits(number):
+def calc_check_digits(number: str) -> str:
     """Calculate the possible check digits for the CUI."""
     number = compact(number)
     weights = (3, 2, 7, 6, 5, 4, 3, 2)
@@ -60,14 +62,14 @@ def calc_check_digits(number):
     return '65432110987'[c] + 'KJIHGFEDCBA'[c]
 
 
-def to_ruc(number):
+def to_ruc(number: str) -> str:
     """Convert the number to a valid RUC."""
     from stdnum.pe import ruc
     number = '10' + compact(number)[:8]
     return number + ruc.calc_check_digit(number)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid CUI. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -80,7 +82,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid CUI. This checks the length,
     formatting and check digit."""
     try:
diff --git a/stdnum/pe/ruc.py b/stdnum/pe/ruc.py
index 7d16524..f5a3f1c 100644
--- a/stdnum/pe/ruc.py
+++ b/stdnum/pe/ruc.py
@@ -40,23 +40,25 @@ InvalidChecksum: ...
 '05414828'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     weights = (5, 4, 3, 2, 7, 6, 5, 4, 3, 2)
     return str((11 - sum(w * int(n) for w, n in zip(weights, number)) % 11) % 
10)
 
 
-def to_dni(number):
+def to_dni(number: str) -> str:
     """Return the DNI (CUI) part of the number for natural persons."""
     number = validate(number)
     if not number.startswith('10'):
@@ -64,7 +66,7 @@ def to_dni(number):
     return number[2:10]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid RUC. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -79,7 +81,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid RUC. This checks the length,
     formatting and check digit."""
     try:
diff --git a/stdnum/pk/cnic.py b/stdnum/pk/cnic.py
index 495399d..47ce8bc 100644
--- a/stdnum/pk/cnic.py
+++ b/stdnum/pk/cnic.py
@@ -46,17 +46,19 @@ More Information:
 '34201-0891231-8'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, '-').strip()
 
 
-def get_gender(number):
+def get_gender(number: str) -> str | None:
     """Get the person's birth gender ('M' or 'F')."""
     number = compact(number)
     if number[-1] in '13579':
@@ -78,13 +80,13 @@ PROVINCES = {
 }
 
 
-def get_province(number):
+def get_province(number: str) -> str | None:
     """Get the person's province."""
     number = compact(number)
     return PROVINCES.get(number[0])
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid CNIC. This checks the length, formatting
     and some digits."""
     number = compact(number)
@@ -99,7 +101,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid CNIC."""
     try:
         return bool(validate(number))
@@ -107,7 +109,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join((number[:5], number[5:12], number[12:]))
diff --git a/stdnum/pl/__init__.py b/stdnum/pl/__init__.py
index 1b5e5db..87f65c6 100644
--- a/stdnum/pl/__init__.py
+++ b/stdnum/pl/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Polish numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.pl import nip as vat  # noqa: F401
diff --git a/stdnum/pl/nip.py b/stdnum/pl/nip.py
index 2ff1c0f..379592f 100644
--- a/stdnum/pl/nip.py
+++ b/stdnum/pl/nip.py
@@ -32,11 +32,13 @@ InvalidChecksum: ...
 '856-734-62-15'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').upper().strip()
@@ -45,13 +47,13 @@ def compact(number):
     return number
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum."""
     weights = (6, 5, 7, 2, 3, 4, 5, 6, 7, -1)
     return sum(w * int(n) for w, n in zip(weights, number)) % 11
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -64,7 +66,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
@@ -72,7 +74,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join((number[0:3], number[3:6], number[6:8], number[8:]))
diff --git a/stdnum/pl/pesel.py b/stdnum/pl/pesel.py
index 370082e..fadedb9 100644
--- a/stdnum/pl/pesel.py
+++ b/stdnum/pl/pesel.py
@@ -47,19 +47,21 @@ datetime.date(2002, 1, 13)
 'F'
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').upper().strip()
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Split the date parts from the number and return the birth date."""
     number = compact(number)
     year = int(number[0:2])
@@ -79,7 +81,7 @@ def get_birth_date(number):
         raise InvalidComponent()
 
 
-def get_gender(number):
+def get_gender(number: str) -> str:
     """Get the person's birth gender ('M' or 'F')."""
     number = compact(number)
     if number[9] in '02468':  # even
@@ -88,7 +90,7 @@ def get_gender(number):
         return 'M'
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for organisations. The number passed
     should not have the check digit included."""
     weights = (1, 3, 7, 9, 1, 3, 7, 9, 1, 3)
@@ -96,7 +98,7 @@ def calc_check_digit(number):
     return str((10 - check) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid national identification number. This
     checks the length, formatting and check digit."""
     number = compact(number)
@@ -110,7 +112,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid national identification number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/pl/regon.py b/stdnum/pl/regon.py
index e738ca7..aac7ed5 100644
--- a/stdnum/pl/regon.py
+++ b/stdnum/pl/regon.py
@@ -50,28 +50,30 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').upper().strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for organisations. The number passed
     should not have the check digit included."""
     if len(number) == 8:
-        weights = (8, 9, 2, 3, 4, 5, 6, 7)
+        weights = [8, 9, 2, 3, 4, 5, 6, 7]
     else:
-        weights = (2, 4, 8, 5, 0, 9, 7, 3, 6, 1, 2, 4, 8)
+        weights = [2, 4, 8, 5, 0, 9, 7, 3, 6, 1, 2, 4, 8]
     check = sum(w * int(n) for w, n in zip(weights, number))
     return str(check % 11 % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid REGON number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -86,7 +88,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid REGON number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/pt/__init__.py b/stdnum/pt/__init__.py
index 7056654..bb7bcd0 100644
--- a/stdnum/pt/__init__.py
+++ b/stdnum/pt/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Portuguese numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.pt import nif as vat  # noqa: F401
diff --git a/stdnum/pt/cc.py b/stdnum/pt/cc.py
index baac0df..855d1f5 100644
--- a/stdnum/pt/cc.py
+++ b/stdnum/pt/cc.py
@@ -42,6 +42,8 @@ InvalidChecksum: ...
 '00000000 0 ZZ4'
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -51,24 +53,25 @@ from stdnum.util import clean
 _cc_re = re.compile(r'^\d*[A-Z0-9]{2}\d$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' ').upper().strip()
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for the number."""
+    def cutoff(x: int) -> int:
+        return x - 9 if x > 9 else x
     alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
-    cutoff = lambda x: x - 9 if x > 9 else x
     s = sum(
         cutoff(alphabet.index(n) * 2) if i % 2 == 0 else alphabet.index(n)
         for i, n in enumerate(number[::-1]))
     return str((10 - s) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid cartao de cidadao number."""
     number = compact(number)
     if not _cc_re.match(number):
@@ -78,7 +81,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid cartao de cidadao number."""
     try:
         return bool(validate(number))
@@ -86,7 +89,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join([number[:-4], number[-4], number[-3:]])
diff --git a/stdnum/pt/nif.py b/stdnum/pt/nif.py
index 77a2808..3a58f97 100644
--- a/stdnum/pt/nif.py
+++ b/stdnum/pt/nif.py
@@ -32,11 +32,13 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -.').upper().strip()
@@ -45,14 +47,14 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     s = sum((9 - i) * int(n) for i, n in enumerate(number))
     return str((11 - s) % 11 % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -65,7 +67,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/py.typed b/stdnum/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/stdnum/py/__init__.py b/stdnum/py/__init__.py
index b7916f7..85ad10d 100644
--- a/stdnum/py/__init__.py
+++ b/stdnum/py/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Paraguayan numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.py import ruc as vat  # noqa: F401
diff --git a/stdnum/py/ruc.py b/stdnum/py/ruc.py
index 0630d96..b280029 100644
--- a/stdnum/py/ruc.py
+++ b/stdnum/py/ruc.py
@@ -50,11 +50,13 @@ InvalidLength: ...
 '80000035-8'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -63,7 +65,7 @@ def compact(number):
     return clean(number, ' -').upper().strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit.
 
     The number passed should not have the check digit included.
@@ -72,7 +74,7 @@ def calc_check_digit(number):
     return str((-s % 11) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Paraguay RUC number.
 
     This checks the length, formatting and check digit.
@@ -87,7 +89,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Paraguay RUC number."""
     try:
         return bool(validate(number))
@@ -95,7 +97,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join([number[:-1], number[-1]])
diff --git a/stdnum/ro/__init__.py b/stdnum/ro/__init__.py
index b0ae7a2..97b3d3e 100644
--- a/stdnum/ro/__init__.py
+++ b/stdnum/ro/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Romanian numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.ro import cf as vat  # noqa: F401
diff --git a/stdnum/ro/cf.py b/stdnum/ro/cf.py
index e6b7511..058287d 100644
--- a/stdnum/ro/cf.py
+++ b/stdnum/ro/cf.py
@@ -30,12 +30,14 @@ The Romanian CF is used for VAT purposes and can be from 2 
to 10 digits long.
 '1630615123457'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.ro import cnp, cui
 from stdnum.util import clean
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').upper().strip()
@@ -45,7 +47,7 @@ def compact(number):
 calc_check_digit = cui.calc_check_digit
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -62,7 +64,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/ro/cnp.py b/stdnum/ro/cnp.py
index 44e02e8..b0ef0ea 100644
--- a/stdnum/ro/cnp.py
+++ b/stdnum/ro/cnp.py
@@ -50,6 +50,8 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum.exceptions import *
@@ -111,13 +113,13 @@ _COUNTIES = {
 }
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for personal codes."""
     # note that this algorithm has not been confirmed by an independent source
     weights = (2, 7, 9, 1, 4, 6, 3, 5, 8, 2, 7, 9)
@@ -125,7 +127,7 @@ def calc_check_digit(number):
     return '1' if check == 10 else str(check)
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Split the date parts from the number and return the birth date."""
     number = compact(number)
     centuries = {
@@ -140,7 +142,7 @@ def get_birth_date(number):
         raise InvalidComponent()
 
 
-def get_county(number):
+def get_county(number: str) -> str:
     """Get the county name from the number"""
     try:
         return _COUNTIES[compact(number)[7:9]]
@@ -148,7 +150,7 @@ def get_county(number):
         raise InvalidComponent()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -169,7 +171,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/ro/cui.py b/stdnum/ro/cui.py
index fbfad98..3c06ba0 100644
--- a/stdnum/ro/cui.py
+++ b/stdnum/ro/cui.py
@@ -43,11 +43,13 @@ InvalidChecksum: ...
 '18547290'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').upper().strip()
@@ -56,7 +58,7 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     weights = (7, 5, 3, 2, 1, 7, 5, 3, 2)
     number = number.zfill(9)
@@ -64,7 +66,7 @@ def calc_check_digit(number):
     return str(check % 11 % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid CUI or CIF number. This checks the 
length,
     formatting and check digit."""
     number = compact(number)
@@ -77,7 +79,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid CUI or CIF number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/ro/onrc.py b/stdnum/ro/onrc.py
index 21860f3..ea33558 100644
--- a/stdnum/ro/onrc.py
+++ b/stdnum/ro/onrc.py
@@ -35,6 +35,8 @@ Traceback (most recent call last):
 InvalidComponent: ...
 """
 
+from __future__ import annotations
+
 import datetime
 import re
 
@@ -56,7 +58,7 @@ _onrc_re = re.compile(r'^[A-Z][0-9]+/[0-9]+/[0-9]+$')
 _counties = set(list(range(1, 41)) + [51, 52])
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = _cleanup_re.sub('/', clean(number).upper().strip())
@@ -73,7 +75,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid ONRC."""
     number = compact(number)
     if not _onrc_re.match(number):
@@ -92,7 +94,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid ONRC."""
     try:
         return bool(validate(number))
diff --git a/stdnum/rs/__init__.py b/stdnum/rs/__init__.py
index 0591656..a5f3c78 100644
--- a/stdnum/rs/__init__.py
+++ b/stdnum/rs/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Serbian numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.rs import pib as vat  # noqa: F401
diff --git a/stdnum/rs/pib.py b/stdnum/rs/pib.py
index d53e211..65a9a74 100644
--- a/stdnum/rs/pib.py
+++ b/stdnum/rs/pib.py
@@ -31,18 +31,20 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_11_10
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -.').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -54,7 +56,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/ru/__init__.py b/stdnum/ru/__init__.py
index 470f21a..3a2cfa2 100644
--- a/stdnum/ru/__init__.py
+++ b/stdnum/ru/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Russian numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.ru import inn as vat  # noqa: F401
diff --git a/stdnum/ru/inn.py b/stdnum/ru/inn.py
index 58cc216..237a6ca 100644
--- a/stdnum/ru/inn.py
+++ b/stdnum/ru/inn.py
@@ -38,32 +38,34 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip()
 
 
-def calc_company_check_digit(number):
+def calc_company_check_digit(number: str) -> str:
     """Calculate the check digit for the 10-digit ИНН for organisations."""
     weights = (2, 4, 10, 3, 5, 9, 4, 6, 8)
     return str(sum(w * int(n) for w, n in zip(weights, number)) % 11 % 10)
 
 
-def calc_personal_check_digits(number):
+def calc_personal_check_digits(number: str) -> str:
     """Calculate the check digits for the 12-digit personal ИНН."""
-    weights = (7, 2, 4, 10, 3, 5, 9, 4, 6, 8)
+    weights = [7, 2, 4, 10, 3, 5, 9, 4, 6, 8]
     d1 = str(sum(w * int(n) for w, n in zip(weights, number)) % 11 % 10)
-    weights = (3, 7, 2, 4, 10, 3, 5, 9, 4, 6, 8)
+    weights = [3, 7, 2, 4, 10, 3, 5, 9, 4, 6, 8]
     d2 = str(sum(w * int(n) for w, n in zip(weights, number[:10] + d1)) % 11 % 
10)
     return d1 + d2
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid ИНН. This checks the length, formatting
     and check digit."""
     number = compact(number)
@@ -81,7 +83,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid ИНН."""
     try:
         return bool(validate(number))
diff --git a/stdnum/ru/ogrn.py b/stdnum/ru/ogrn.py
index e3d6558..9ac4b6c 100644
--- a/stdnum/ru/ogrn.py
+++ b/stdnum/ru/ogrn.py
@@ -44,17 +44,19 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ')
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the control digit of the OGRN based on its length."""
     if len(number) == 13:
         return str(int(number[:-1]) % 11 % 10)
@@ -62,7 +64,7 @@ def calc_check_digit(number):
         return str(int(number[:-1]) % 13)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Determine if the given number is a valid OGRN."""
     number = compact(number)
     if not isdigits(number):
@@ -80,7 +82,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid OGRN."""
     try:
         return bool(validate(number))
diff --git a/stdnum/se/__init__.py b/stdnum/se/__init__.py
index 23b98c5..f5f004b 100644
--- a/stdnum/se/__init__.py
+++ b/stdnum/se/__init__.py
@@ -20,6 +20,8 @@
 
 """Collection of Swedish numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.se import personnummer as personalid  # noqa: F401
 from stdnum.se import postnummer as postal_code  # noqa: F401
diff --git a/stdnum/se/orgnr.py b/stdnum/se/orgnr.py
index 2aea9eb..11628ee 100644
--- a/stdnum/se/orgnr.py
+++ b/stdnum/se/orgnr.py
@@ -36,18 +36,20 @@ InvalidChecksum: ...
 '123456-7897'
 """
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -.').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid organisation number. This checks
     the length, formatting and check digit."""
     number = compact(number)
@@ -58,7 +60,7 @@ def validate(number):
     return luhn.validate(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid organisation number"""
     try:
         return bool(validate(number))
@@ -66,7 +68,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return number[:6] + '-' + number[6:]
diff --git a/stdnum/se/personnummer.py b/stdnum/se/personnummer.py
index 8829ef7..4dff3b1 100644
--- a/stdnum/se/personnummer.py
+++ b/stdnum/se/personnummer.py
@@ -45,6 +45,8 @@ datetime.date(1981, 12, 28)
 '880320-0016'
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum import luhn
@@ -52,7 +54,7 @@ from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' :')
@@ -61,7 +63,7 @@ def compact(number):
     return number[:-5].replace('-', '').replace('+', '') + number[-5:]
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Determine the birth date from the number.
 
     For people aged 100 and up, the minus/dash in the personnummer is changed 
to a plus
@@ -90,7 +92,7 @@ def get_birth_date(number):
         raise InvalidComponent()
 
 
-def get_gender(number):
+def get_gender(number: str) -> str:
     """Get the person's birth gender ('M' or 'F')."""
     number = compact(number)
     if int(number[-2]) % 2:
@@ -99,7 +101,7 @@ def get_gender(number):
         return 'F'
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid identity number."""
     number = compact(number)
     if len(number) not in (11, 13):
@@ -112,7 +114,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid identity number."""
     try:
         return bool(validate(number))
@@ -120,6 +122,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/se/postnummer.py b/stdnum/se/postnummer.py
index b2246db..27ba90f 100644
--- a/stdnum/se/postnummer.py
+++ b/stdnum/se/postnummer.py
@@ -39,11 +39,13 @@ InvalidLength: ...
 '114 18'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').upper().strip()
@@ -52,7 +54,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is in the correct format. This currently does not
     check whether the code corresponds to a real address."""
     number = compact(number)
@@ -63,7 +65,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid postal code."""
     try:
         return bool(validate(number))
@@ -71,7 +73,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '%s %s' % (number[:3], number[3:])
diff --git a/stdnum/se/vat.py b/stdnum/se/vat.py
index 9dcd356..4c2e7df 100644
--- a/stdnum/se/vat.py
+++ b/stdnum/se/vat.py
@@ -32,12 +32,14 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.se import orgnr
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -.').upper().strip()
@@ -46,7 +48,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -56,7 +58,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/sg/__init__.py b/stdnum/sg/__init__.py
index 1d3e79d..bf29be6 100644
--- a/stdnum/sg/__init__.py
+++ b/stdnum/sg/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Singapore numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.sg import uen as vat  # noqa: F401
diff --git a/stdnum/sg/uen.py b/stdnum/sg/uen.py
index ea0a1b2..602daee 100644
--- a/stdnum/sg/uen.py
+++ b/stdnum/sg/uen.py
@@ -60,6 +60,8 @@ InvalidLength: ...
 # start with an F for foreign companies but it is unclear whether this is
 # still current and not even examples of these numbers could be found.
 
+from __future__ import annotations
+
 from datetime import datetime
 
 from stdnum.exceptions import *
@@ -74,7 +76,7 @@ OTHER_UEN_ENTITY_TYPES = (
 )
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This converts to uppercase and removes surrounding whitespace. It
@@ -84,14 +86,14 @@ def compact(number):
     return clean(number).upper().strip()
 
 
-def calc_business_check_digit(number):
+def calc_business_check_digit(number: str) -> str:
     """Calculate the check digit for the Business (ROB) number."""
     number = compact(number)
     weights = (10, 4, 9, 3, 8, 2, 7, 1)
     return 'XMKECAWLJDB'[sum(int(n) * w for n, w in zip(number, weights)) % 11]
 
 
-def _validate_business(number):
+def _validate_business(number: str) -> str:
     """Perform validation on UEN - Business (ROB) numbers."""
     if not isdigits(number[:-1]):
         raise InvalidFormat()
@@ -102,14 +104,14 @@ def _validate_business(number):
     return number
 
 
-def calc_local_company_check_digit(number):
+def calc_local_company_check_digit(number: str) -> str:
     """Calculate the check digit for the Local Company (ROC) number."""
     number = compact(number)
     weights = (10, 8, 6, 4, 9, 7, 5, 3, 1)
     return 'ZKCMDNERGWH'[sum(int(n) * w for n, w in zip(number, weights)) % 11]
 
 
-def _validate_local_company(number):
+def _validate_local_company(number: str) -> str:
     """Perform validation on UEN - Local Company (ROC) numbers."""
     if not isdigits(number[:-1]):
         raise InvalidFormat()
@@ -121,7 +123,7 @@ def _validate_local_company(number):
     return number
 
 
-def calc_other_check_digit(number):
+def calc_other_check_digit(number: str) -> str:
     """Calculate the check digit for the other entities number."""
     number = compact(number)
     alphabet = 'ABCDEFGHJKLMNPQRSTUVWX0123456789'
@@ -129,7 +131,7 @@ def calc_other_check_digit(number):
     return alphabet[(sum(alphabet.index(n) * w for n, w in zip(number, 
weights)) - 5) % 11]
 
 
-def _validate_other(number):
+def _validate_other(number: str) -> str:
     """Perform validation on other UEN numbers."""
     if number[0] not in ('R', 'S', 'T'):
         raise InvalidComponent()
@@ -147,7 +149,7 @@ def _validate_other(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Singapore UEN number."""
     number = compact(number)
     if len(number) not in (9, 10):
@@ -159,7 +161,7 @@ def validate(number):
     return _validate_other(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Singapore UEN number."""
     try:
         return bool(validate(number))
@@ -167,6 +169,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/si/__init__.py b/stdnum/si/__init__.py
index 98349b8..fe2cc47 100644
--- a/stdnum/si/__init__.py
+++ b/stdnum/si/__init__.py
@@ -21,6 +21,8 @@
 
 """Collection of Slovenian numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.si import ddv as vat  # noqa: F401
 from stdnum.si import emso as personalid  # noqa: F401
diff --git a/stdnum/si/ddv.py b/stdnum/si/ddv.py
index bb6d986..329624b 100644
--- a/stdnum/si/ddv.py
+++ b/stdnum/si/ddv.py
@@ -32,11 +32,13 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').upper().strip()
@@ -45,7 +47,7 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     check = (11 - sum((8 - i) * int(n) for i, n in enumerate(number)) % 11)
@@ -53,7 +55,7 @@ def calc_check_digit(number):
     return '0' if check == 10 else str(check)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -66,7 +68,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/si/emso.py b/stdnum/si/emso.py
index 2a12956..6a47789 100644
--- a/stdnum/si/emso.py
+++ b/stdnum/si/emso.py
@@ -41,26 +41,28 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' ').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     weights = (7, 6, 5, 4, 3, 2, 7, 6, 5, 4, 3, 2)
     total = sum(int(n) * w for n, w in zip(number, weights))
     return str(-total % 11 % 10)
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Return date of birth from valid EMŠO."""
     number = compact(number)
     day = int(number[:2])
@@ -76,7 +78,7 @@ def get_birth_date(number):
         raise InvalidComponent()
 
 
-def get_gender(number):
+def get_gender(number: str) -> str:
     """Get the person's birth gender ('M' or 'F')."""
     number = compact(number)
     if int(number[9:12]) < 500:
@@ -85,12 +87,12 @@ def get_gender(number):
         return 'F'
 
 
-def get_region(number):
+def get_region(number: str) -> str:
     """Return (political) region from valid EMŠO."""
     return number[7:9]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid EMŠO number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -104,7 +106,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid ID. This checks the length,
     formatting and check digit."""
     try:
@@ -113,6 +115,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/si/maticna.py b/stdnum/si/maticna.py
index 6aa9135..22f1aae 100644
--- a/stdnum/si/maticna.py
+++ b/stdnum/si/maticna.py
@@ -43,13 +43,15 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, '. ').strip().upper()
@@ -58,7 +60,7 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     weights = (7, 6, 5, 4, 3, 2)
     total = sum(int(n) * w for n, w in zip(number, weights))
@@ -68,7 +70,7 @@ def calc_check_digit(number):
     return str(remainder % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Corporate Registration number. This
     checks the length and check digit."""
     number = compact(number)
@@ -83,7 +85,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if provided is valid ID. This checks the length,
     formatting and check digit."""
     try:
diff --git a/stdnum/sk/__init__.py b/stdnum/sk/__init__.py
index c2e257e..27f389b 100644
--- a/stdnum/sk/__init__.py
+++ b/stdnum/sk/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Slovak numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.sk import dph as vat  # noqa: F401
diff --git a/stdnum/sk/dph.py b/stdnum/sk/dph.py
index c76f526..4313e16 100644
--- a/stdnum/sk/dph.py
+++ b/stdnum/sk/dph.py
@@ -31,12 +31,14 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.sk import rc
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     number = clean(number, ' -').upper().strip()
@@ -45,12 +47,12 @@ def compact(number):
     return number
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the checksum."""
     return int(number) % 11
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -68,7 +70,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/sk/rc.py b/stdnum/sk/rc.py
index f43dc79..9ef2208 100644
--- a/stdnum/sk/rc.py
+++ b/stdnum/sk/rc.py
@@ -49,6 +49,9 @@ InvalidLength: ...
 
 # since this number is essentially the same as the Czech counterpart
 # (until 1993 the Czech Republic and Slovakia were one country)
+
+from __future__ import annotations
+
 from stdnum.cz.rc import compact, format, is_valid, validate
 
 
diff --git a/stdnum/sm/__init__.py b/stdnum/sm/__init__.py
index 28c385d..acad26a 100644
--- a/stdnum/sm/__init__.py
+++ b/stdnum/sm/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of San Marino numbers."""
 
+from __future__ import annotations
+
 # provide vat as an alias
 from stdnum.sm import coe as vat  # noqa: F401
diff --git a/stdnum/sm/coe.py b/stdnum/sm/coe.py
index 721bda5..d0edb06 100644
--- a/stdnum/sm/coe.py
+++ b/stdnum/sm/coe.py
@@ -39,6 +39,8 @@ Traceback (most recent call last):
 InvalidLength: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
@@ -51,13 +53,13 @@ _lownumbers = set((
     87, 88, 91, 92, 94, 95, 96, 97, 99))
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips
     surrounding whitespace and separation dash."""
     return clean(number, '.').strip().lstrip('0')
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid COE. This checks the length and
     formatting."""
     number = compact(number)
@@ -70,7 +72,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid COE."""
     try:
         return bool(validate(number))
diff --git a/stdnum/sv/__init__.py b/stdnum/sv/__init__.py
index dd63b08..074a6e9 100644
--- a/stdnum/sv/__init__.py
+++ b/stdnum/sv/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of El Salvador numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.sv import nit as vat  # noqa: F401
diff --git a/stdnum/sv/nit.py b/stdnum/sv/nit.py
index 24124e9..38c2522 100644
--- a/stdnum/sv/nit.py
+++ b/stdnum/sv/nit.py
@@ -61,11 +61,13 @@ InvalidLength: ...
 '0614-050707-104-8'
 """  # noqa: E501
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -77,7 +79,7 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     # Old NIT
     if number[10:13] <= '100':
@@ -90,7 +92,7 @@ def calc_check_digit(number):
     return str((-total % 11) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid El Salvador NIT number.
 
     This checks the length, formatting and check digit.
@@ -107,7 +109,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid El Salvador NIT number."""
     try:
         return bool(validate(number))
@@ -115,7 +117,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join([number[:4], number[4:-4], number[-4:-1], number[-1]])
diff --git a/stdnum/th/moa.py b/stdnum/th/moa.py
index 9538021..41cdaae 100644
--- a/stdnum/th/moa.py
+++ b/stdnum/th/moa.py
@@ -45,6 +45,8 @@ InvalidChecksum: ...
 '0-99-3-000-13397-8'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.th import pin
 from stdnum.util import clean, isdigits
@@ -57,13 +59,13 @@ __all__ = ['compact', 'calc_check_digit', 'validate', 
'is_valid', 'format']
 calc_check_digit = pin.calc_check_digit
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid MOA Number. This checks the length,
     formatting, component and check digit."""
     number = compact(number)
@@ -78,7 +80,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check whether the number is valid."""
     try:
         return bool(validate(number))
@@ -86,7 +88,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join((
diff --git a/stdnum/th/pin.py b/stdnum/th/pin.py
index 77afe40..edc7a04 100644
--- a/stdnum/th/pin.py
+++ b/stdnum/th/pin.py
@@ -44,23 +44,25 @@ InvalidChecksum: ...
 '7-1006-00445-63-5'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     s = sum((2 - i) * int(n) for i, n in enumerate(number[:12])) % 11
     return str((1 - s) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid PIN. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -75,7 +77,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check whether the number is valid."""
     try:
         return bool(validate(number))
@@ -83,7 +85,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join((
diff --git a/stdnum/th/tin.py b/stdnum/th/tin.py
index cc51387..6a4d365 100644
--- a/stdnum/th/tin.py
+++ b/stdnum/th/tin.py
@@ -43,6 +43,8 @@ InvalidFormat: ...
 '0-99-3-000-13397-8'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.th import moa, pin
 from stdnum.util import clean
@@ -51,13 +53,13 @@ from stdnum.util import clean
 _tin_modules = (moa, pin)
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').strip()
 
 
-def tin_type(number):
+def tin_type(number: str) -> str | None:
     """Return a TIN type which this number is valid."""
     number = compact(number)
     for mod in _tin_modules:
@@ -67,19 +69,19 @@ def tin_type(number):
     return None
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid TIN. This searches for the proper
     sub-type and validates using that."""
     for mod in _tin_modules:
         try:
-            return mod.validate(number)
+            return mod.validate(number)  # type: ignore[no-any-return]
         except ValidationError:
             pass  # try next module
     # fallback
     raise InvalidFormat()
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check whether the number is valid."""
     try:
         return bool(validate(number))
@@ -87,9 +89,9 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     for mod in _tin_modules:
         if mod.is_valid(number):
-            return mod.format(number)
+            return mod.format(number)  # type: ignore[no-any-return]
     return number
diff --git a/stdnum/tn/__init__.py b/stdnum/tn/__init__.py
index edbf777..3ed9e5a 100644
--- a/stdnum/tn/__init__.py
+++ b/stdnum/tn/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Tunisian numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.tn import mf as vat  # noqa: F401
diff --git a/stdnum/tn/mf.py b/stdnum/tn/mf.py
index 90cb3cf..609708d 100644
--- a/stdnum/tn/mf.py
+++ b/stdnum/tn/mf.py
@@ -63,6 +63,8 @@ InvalidFormat: ...
 '1496298/T/P/N/000'
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -76,7 +78,7 @@ _VALID_TVA_CODES = ('A', 'P', 'B', 'D', 'N')
 _VALID_CATEGORY_CODES = ('M', 'P', 'C', 'N', 'E')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators, removes surrounding
@@ -90,7 +92,7 @@ def compact(number):
     return number
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Tunisia MF number.
 
     This checks the length and formatting.
@@ -115,7 +117,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Tunisia MF number."""
     try:
         return bool(validate(number))
@@ -123,7 +125,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     result = compact(number)
     if len(result) == 8:
diff --git a/stdnum/tr/__init__.py b/stdnum/tr/__init__.py
index ebb92d3..b7e1b22 100644
--- a/stdnum/tr/__init__.py
+++ b/stdnum/tr/__init__.py
@@ -19,4 +19,7 @@
 # 02110-1301 USA
 
 """Collection of Turkish numbers."""
+
+from __future__ import annotations
+
 from stdnum.tr import vkn as vat  # noqa: F401
diff --git a/stdnum/tr/tckimlik.py b/stdnum/tr/tckimlik.py
index 9a5df41..bca5a80 100644
--- a/stdnum/tr/tckimlik.py
+++ b/stdnum/tr/tckimlik.py
@@ -45,6 +45,8 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, get_soap_client, isdigits
 
@@ -53,13 +55,13 @@ tckimlik_wsdl = 
'https://tckimlik.nvi.gov.tr/Service/KPSPublic.asmx?WSDL'
 """The WSDL URL of the T.C. Kimlik validation service."""
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number).strip()
 
 
-def calc_check_digits(number):
+def calc_check_digits(number: str) -> str:
     """Calculate the check digits for the specified number. The number
     passed should not have the check digit included."""
     check1 = (10 - sum((3, 1)[i % 2] * int(n)
@@ -68,7 +70,7 @@ def calc_check_digits(number):
     return '%d%d' % (check1, check2)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid T.C. Kimlik number. This checks the
     length and check digits."""
     number = compact(number)
@@ -81,7 +83,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid T.C. Kimlik number."""
     try:
         return bool(validate(number))
@@ -89,7 +91,14 @@ def is_valid(number):
         return False
 
 
-def check_kps(number, name, surname, birth_year, timeout=30, verify=True):  # 
pragma: no cover
+def check_kps(
+    number: str,
+    name: str,
+    surname: str,
+    birth_year: int,
+    timeout: float = 30,
+    verify: bool | str = True,
+) -> bool:  # pragma: no cover
     """Use the T.C. Kimlik validation service to check the provided number.
 
     Query the online T.C. Kimlik validation service run by the Directorate
@@ -110,5 +119,5 @@ def check_kps(number, name, surname, birth_year, 
timeout=30, verify=True):  # pr
     result = client.TCKimlikNoDogrula(
         TCKimlikNo=number, Ad=name, Soyad=surname, DogumYili=birth_year)
     if hasattr(result, 'get'):
-        return result.get('TCKimlikNoDogrulaResult')
-    return result
+        return result.get('TCKimlikNoDogrulaResult')  # type: 
ignore[no-any-return]
+    return result  # type: ignore[no-any-return]
diff --git a/stdnum/tr/vkn.py b/stdnum/tr/vkn.py
index e23cb91..406620a 100644
--- a/stdnum/tr/vkn.py
+++ b/stdnum/tr/vkn.py
@@ -40,17 +40,19 @@ Traceback (most recent call last):
 InvalidLength: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number).strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for the specified number."""
     s = 0
     for i, n in enumerate(reversed(number[:9]), 1):
@@ -61,7 +63,7 @@ def calc_check_digit(number):
     return str((10 - s) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Vergi Kimlik Numarası. This checks the
     length and check digits."""
     number = compact(number)
@@ -74,7 +76,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Vergi Kimlik Numarası. This checks the
     length and check digits."""
     try:
diff --git a/stdnum/tw/__init__.py b/stdnum/tw/__init__.py
index d79cc3d..1adbfd3 100644
--- a/stdnum/tw/__init__.py
+++ b/stdnum/tw/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Taiwanese numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.tw import ubn as vat  # noqa: F401
diff --git a/stdnum/tw/ubn.py b/stdnum/tw/ubn.py
index efae740..2e88a8d 100644
--- a/stdnum/tw/ubn.py
+++ b/stdnum/tw/ubn.py
@@ -43,11 +43,13 @@ InvalidLength: ...
 '00501503'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -56,7 +58,7 @@ def compact(number):
     return clean(number, ' -').strip()
 
 
-def calc_checksum(number):
+def calc_checksum(number: str) -> int:
     """Calculate the checksum over the number."""
     # convert to numeric first, then sum individual digits
     weights = (1, 2, 1, 2, 1, 2, 4, 1)
@@ -64,7 +66,7 @@ def calc_checksum(number):
     return sum(int(n) for n in number) % 10
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Taiwan UBN number.
 
     This checks the length, formatting and check digit.
@@ -80,7 +82,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Taiwan UBN number."""
     try:
         return bool(validate(number))
@@ -88,6 +90,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/ua/edrpou.py b/stdnum/ua/edrpou.py
index a286f53..2771cbc 100644
--- a/stdnum/ua/edrpou.py
+++ b/stdnum/ua/edrpou.py
@@ -44,30 +44,32 @@ InvalidLength: ...
 '32855961'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation."""
     return clean(number, ' ').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for number."""
-    weights = (1, 2, 3, 4, 5, 6, 7)
+    weights = [1, 2, 3, 4, 5, 6, 7]
     if number[0] in '345':
-        weights = (7, 1, 2, 3, 4, 5, 6)
+        weights = [7, 1, 2, 3, 4, 5, 6]
     total = sum(w * int(n) for w, n in zip(weights, number))
     if total % 11 < 10:
         return str(total % 11)
     # Calculate again with other weights
-    weights = tuple(w + 2 for w in weights)
+    weights = [w + 2 for w in weights]
     total = sum(w * int(n) for w, n in zip(weights, number))
     return str(total % 11 % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Ukraine EDRPOU (ЄДРПОУ) number.
 
     This checks the length, formatting and check digit.
@@ -82,7 +84,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Ukraine EDRPOU (ЄДРПОУ) number."""
     try:
         return bool(validate(number))
@@ -90,6 +92,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/ua/rntrc.py b/stdnum/ua/rntrc.py
index 07b64de..dcbba5d 100644
--- a/stdnum/ua/rntrc.py
+++ b/stdnum/ua/rntrc.py
@@ -43,23 +43,25 @@ InvalidLength: ...
 '2530414071'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation."""
     return clean(number, ' ').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for number."""
     weights = (-1, 5, 7, 9, 4, 6, 10, 5, 7)
     total = sum(w * int(n) for w, n in zip(weights, number))
     return str((total % 11) % 10)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Ukraine RNTRC (РНОКПП) number.
 
     This checks the length, formatting and check digit.
@@ -74,7 +76,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Ukraine RNTRC (РНОКПП) number."""
     try:
         return bool(validate(number))
@@ -82,6 +84,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/stdnum/us/atin.py b/stdnum/us/atin.py
index 4ff5953..195ab06 100644
--- a/stdnum/us/atin.py
+++ b/stdnum/us/atin.py
@@ -35,6 +35,8 @@ InvalidFormat: ...
 '123'
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -45,13 +47,13 @@ from stdnum.util import clean
 _atin_re = re.compile(r'^[0-9]{3}-?[0-9]{2}-?[0-9]{4}$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, '-').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid ATIN. This checks the length and
     formatting if it is present."""
     match = _atin_re.search(clean(number, '').strip())
@@ -61,7 +63,7 @@ def validate(number):
     return compact(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid ATIN."""
     try:
         return bool(validate(number))
@@ -69,7 +71,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     if len(number) == 9:
         number = number[:3] + '-' + number[3:5] + '-' + number[5:]
diff --git a/stdnum/us/ein.py b/stdnum/us/ein.py
index 75d178c..9fee450 100644
--- a/stdnum/us/ein.py
+++ b/stdnum/us/ein.py
@@ -42,6 +42,8 @@ InvalidComponent: ...
 '123'
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -52,13 +54,13 @@ from stdnum.util import clean
 _ein_re = re.compile(r'^(?P<area>[0-9]{2})-?(?P<group>[0-9]{7})$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, '-').strip()
 
 
-def get_campus(number):
+def get_campus(number: str) -> str:
     """Determine the Campus or other location that issued the EIN."""
     from stdnum import numdb
     number = compact(number)
@@ -68,7 +70,7 @@ def get_campus(number):
     return results['campus']
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid EIN. This checks the length, groups and
     formatting if it is present."""
     match = _ein_re.search(clean(number, '').strip())
@@ -78,7 +80,7 @@ def validate(number):
     return compact(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid EIN."""
     try:
         return bool(validate(number))
@@ -86,7 +88,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     if len(number) == 9:
         number = number[:2] + '-' + number[2:]
diff --git a/stdnum/us/itin.py b/stdnum/us/itin.py
index 0d1aa41..b981075 100644
--- a/stdnum/us/itin.py
+++ b/stdnum/us/itin.py
@@ -49,6 +49,8 @@ InvalidComponent: ...
 '123'
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -63,13 +65,13 @@ _itin_re = 
re.compile(r'^(?P<area>[0-9]{3})-?(?P<group>[0-9]{2})-?[0-9]{4}$')
 _allowed_groups = set((str(x) for x in range(70, 100) if x not in (89, 93)))
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, '-').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid ITIN. This checks the length, groups
     and formatting if it is present."""
     match = _itin_re.search(clean(number, '').strip())
@@ -82,7 +84,7 @@ def validate(number):
     return compact(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid ITIN."""
     try:
         return bool(validate(number))
@@ -90,7 +92,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     if len(number) == 9:
         number = number[:3] + '-' + number[3:5] + '-' + number[5:]
diff --git a/stdnum/us/ptin.py b/stdnum/us/ptin.py
index 7d60f6a..66d31f8 100644
--- a/stdnum/us/ptin.py
+++ b/stdnum/us/ptin.py
@@ -33,6 +33,8 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -43,13 +45,13 @@ from stdnum.util import clean
 _ptin_re = re.compile(r'^P[0-9]{8}$')
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, '-').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid PTIN. This checks the length, groups
     and formatting if it is present."""
     number = compact(number).upper()
@@ -59,7 +61,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid ATIN."""
     try:
         return bool(validate(number))
diff --git a/stdnum/us/rtn.py b/stdnum/us/rtn.py
index eb88926..f3cadd0 100644
--- a/stdnum/us/rtn.py
+++ b/stdnum/us/rtn.py
@@ -42,18 +42,20 @@ Traceback (most recent call last):
 InvalidChecksum: ..
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any surrounding whitespace."""
     number = clean(number).strip()
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit. The number passed should not have the
     check digit included."""
     digits = [int(c) for c in number]
@@ -65,7 +67,7 @@ def calc_check_digit(number):
     return str(checksum)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid routing number. This checks the length
     and check digit."""
     number = compact(number)
@@ -78,7 +80,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid RTN."""
     try:
         return bool(validate(number))
diff --git a/stdnum/us/ssn.py b/stdnum/us/ssn.py
index 78d538e..7deb0b5 100644
--- a/stdnum/us/ssn.py
+++ b/stdnum/us/ssn.py
@@ -60,6 +60,8 @@ InvalidComponent: ...
 '111-22-3333'
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
@@ -74,13 +76,13 @@ _ssn_re = re.compile(
 _ssn_blacklist = set(('078-05-1120', '457-55-5462', '219-09-9999'))
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, '-').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid SSN. This checks the length, groups and
     formatting if it is present."""
     match = _ssn_re.search(clean(number, '').strip())
@@ -100,7 +102,7 @@ def validate(number):
     return compact(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid SSN."""
     try:
         return bool(validate(number))
@@ -108,7 +110,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     if len(number) == 9:
         number = number[:3] + '-' + number[3:5] + '-' + number[5:]
diff --git a/stdnum/us/tin.py b/stdnum/us/tin.py
index 72ccddc..d0fcc2b 100644
--- a/stdnum/us/tin.py
+++ b/stdnum/us/tin.py
@@ -48,6 +48,8 @@ InvalidFormat: ...
 '123-456'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.us import atin, ein, itin, ptin, ssn
 from stdnum.util import clean
@@ -56,25 +58,25 @@ from stdnum.util import clean
 _tin_modules = (ssn, itin, ein, ptin, atin)
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, '-').strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid TIN. This searches for the proper
     sub-type and validates using that."""
     for mod in _tin_modules:
         try:
-            return mod.validate(number)
+            return mod.validate(number)  # type: ignore[no-any-return]
         except ValidationError:
             pass  # try next module
     # fallback
     raise InvalidFormat()
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid TIN."""
     try:
         return bool(validate(number))
@@ -82,7 +84,7 @@ def is_valid(number):
         return False
 
 
-def guess_type(number):
+def guess_type(number: str) -> list[str]:
     """Return a list of possible TIN types for which this number is
     valid.."""
     return [mod.__name__.rsplit('.', 1)[-1]
@@ -90,9 +92,9 @@ def guess_type(number):
             if mod.is_valid(number)]
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     for mod in _tin_modules:
         if mod.is_valid(number) and hasattr(mod, 'format'):
-            return mod.format(number)
+            return mod.format(number)  # type: ignore[no-any-return]
     return number
diff --git a/stdnum/util.py b/stdnum/util.py
index 4da281e..abbd5d6 100644
--- a/stdnum/util.py
+++ b/stdnum/util.py
@@ -25,6 +25,8 @@ guaranteed to remain stable and as such not part of the 
public API of
 stdnum.
 """
 
+from __future__ import annotations
+
 import pkgutil
 import pydoc
 import re
@@ -36,6 +38,29 @@ import warnings
 from stdnum.exceptions import *
 
 
+TYPE_CHECKING = False
+if TYPE_CHECKING:  # pragma: no cover (only used when type checking)
+    from collections.abc import Generator
+    from typing import Any, Protocol
+
+    class NumberValidationModule(Protocol):
+        """Minimal interface for a number validation module."""
+
+        def compact(self, number: str) -> str:
+            """Convert the number to the minimal representation."""
+
+        def validate(self, number: str) -> str:
+            """Check if the number provided is a valid number of its type."""
+
+        def is_valid(self, number: str) -> bool:
+            """Check if the number provided is a valid number of its type."""
+
+
+else:
+
+    NumberValidationModule = None
+
+
 # Regular expression to match doctests in docstrings
 _strip_doctest_re = re.compile(r'^>>> .*\Z', re.DOTALL | re.MULTILINE)
 
@@ -44,7 +69,7 @@ _strip_doctest_re = re.compile(r'^>>> .*\Z', re.DOTALL | 
re.MULTILINE)
 _digits_re = re.compile(r'^[0-9]+$')
 
 
-def _mk_char_map(mapping):
+def _mk_char_map(mapping: dict[str, str]) -> Generator[tuple[str, str]]:
     """Transform a dictionary with comma separated uniode character names
     to tuples with unicode characters as key."""
     for key, value in mapping.items():
@@ -151,12 +176,12 @@ _char_map = dict(_mk_char_map({
 }))
 
 
-def _clean_chars(number):
+def _clean_chars(number: str) -> str:
     """Replace various Unicode characters with their ASCII counterpart."""
     return ''.join(_char_map.get(x, x) for x in number)
 
 
-def clean(number, deletechars=''):
+def clean(number: str, deletechars: str = '') -> str:
     """Remove the specified characters from the supplied number.
 
     >>> clean('123-456:78 9', ' -:')
@@ -172,14 +197,14 @@ def clean(number, deletechars=''):
     return ''.join(x for x in number if x not in deletechars)
 
 
-def isdigits(number):
+def isdigits(number: str) -> bool:
     """Check whether the provided string only consists of digits."""
     # This function is meant to replace str.isdigit() which will also return
     # True for all kind of unicode digits which is generally not what we want
     return bool(_digits_re.match(number))
 
 
-def to_unicode(text):
+def to_unicode(text: str | bytes) -> str:
     """DEPRECATED: Will be removed in an upcoming release."""  # noqa: D40
     warnings.warn(
         'to_unicode() will be removed in an upcoming release',
@@ -192,7 +217,7 @@ def to_unicode(text):
     return text
 
 
-def get_number_modules(base='stdnum'):
+def get_number_modules(base: str = 'stdnum') -> 
Generator[NumberValidationModule]:
     """Yield all the number validation modules under the specified module."""
     __import__(base)
     module = sys.modules[base]
@@ -207,19 +232,19 @@ def get_number_modules(base='stdnum'):
                 yield module
 
 
-def get_module_name(module):
+def get_module_name(module: object) -> str:
     """Return the short description of the number."""
     return pydoc.splitdoc(pydoc.getdoc(module))[0].strip('.')
 
 
-def get_module_description(module):
+def get_module_description(module: object) -> str:
     """Return a description of the number."""
     doc = pydoc.splitdoc(pydoc.getdoc(module))[1]
     # remove the doctests
     return _strip_doctest_re.sub('', doc).strip()
 
 
-def get_cc_module(cc, name):
+def get_cc_module(cc: str, name: str) -> NumberValidationModule | None:
     """Find the country-specific named module."""
     cc = cc.lower()
     # add suffix for python reserved words
@@ -236,25 +261,34 @@ def get_cc_module(cc, name):
 _soap_clients = {}
 
 
-def _get_zeep_soap_client(wsdlurl, timeout, verify):  # pragma: no cover (not 
part of normal test suite)
+def _get_zeep_soap_client(
+    wsdlurl: str,
+    timeout: float,
+    verify: bool | str,
+) -> Any:  # pragma: no cover (not part of normal test suite)
     from requests import Session
     from zeep import CachingClient
     from zeep.transports import Transport
     session = Session()
     session.verify = verify
-    transport = Transport(operation_timeout=timeout, timeout=timeout, 
session=session)
-    return CachingClient(wsdlurl, transport=transport).service
+    transport = Transport(operation_timeout=timeout, timeout=timeout, 
session=session)  # type: ignore[no-untyped-call]
+    return CachingClient(wsdlurl, transport=transport).service  # type: 
ignore[no-untyped-call]
 
 
-def _get_suds_soap_client(wsdlurl, timeout, verify):  # pragma: no cover (not 
part of normal test suite)
+def _get_suds_soap_client(
+    wsdlurl: str,
+    timeout: float,
+    verify: bool | str,
+) -> Any:  # pragma: no cover (not part of normal test suite)
+    import os.path
     from urllib.request import HTTPSHandler, getproxies
 
-    from suds.client import Client
-    from suds.transport.http import HttpTransport
+    from suds.client import Client  # type: ignore
+    from suds.transport.http import HttpTransport  # type: ignore
 
-    class CustomSudsTransport(HttpTransport):
+    class CustomSudsTransport(HttpTransport):  # type: ignore[misc]
 
-        def u2handlers(self):
+        def u2handlers(self):  # type: ignore[no-untyped-def]
             handlers = super(CustomSudsTransport, self).u2handlers()
             if isinstance(verify, str):
                 if not os.path.isdir(verify):
@@ -274,8 +308,14 @@ def _get_suds_soap_client(wsdlurl, timeout, verify):  # 
pragma: no cover (not pa
     return Client(wsdlurl, proxy=getproxies(), timeout=timeout, 
transport=CustomSudsTransport()).service
 
 
-def _get_pysimplesoap_soap_client(wsdlurl, timeout, verify):  # pragma: no 
cover (not part of normal test suite)
-    from pysimplesoap.client import SoapClient
+def _get_pysimplesoap_soap_client(
+    wsdlurl: str,
+    timeout: float,
+    verify: bool | str,
+) -> Any:  # pragma: no cover (not part of normal test suite)
+    from urllib.request import getproxies
+
+    from pysimplesoap.client import SoapClient  # type: ignore
     if verify is False:
         raise ValueError('PySimpleSOAP does not support verify=False')
     kwargs = {}
@@ -287,7 +327,11 @@ def _get_pysimplesoap_soap_client(wsdlurl, timeout, 
verify):  # pragma: no cover
     return SoapClient(wsdl=wsdlurl, proxy=getproxies(), timeout=timeout, 
**kwargs)
 
 
-def get_soap_client(wsdlurl, timeout=30, verify=True):  # pragma: no cover 
(not part of normal test suite)
+def get_soap_client(
+    wsdlurl: str,
+    timeout: float = 30,
+    verify: bool | str = True,
+) -> Any:  # pragma: no cover (not part of normal test suite)
     """Get a SOAP client for performing requests. The client is cached. The
     timeout is in seconds. The verify parameter is either True (the default), 
False
     (to disabled certificate validation) or string value pointing to a CA 
certificate
diff --git a/stdnum/uy/__init__.py b/stdnum/uy/__init__.py
index 3cfea91..bbc20d2 100644
--- a/stdnum/uy/__init__.py
+++ b/stdnum/uy/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Uruguayan numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.uy import rut as vat  # noqa: F401
diff --git a/stdnum/uy/rut.py b/stdnum/uy/rut.py
index 769578d..1320c18 100644
--- a/stdnum/uy/rut.py
+++ b/stdnum/uy/rut.py
@@ -48,6 +48,8 @@ InvalidLength: ...
 '21-100342-001-7'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
@@ -58,7 +60,7 @@ from stdnum.util import clean, isdigits
 # https://servicios.dgi.gub.uy/ServiciosEnLinea/ampliar/servicios-automatizados
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -70,14 +72,14 @@ def compact(number):
     return number
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     weights = (4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2)
     total = sum(int(n) * w for w, n in zip(weights, number))
     return str(-total % 11)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Uruguay RUT number.
 
     This checks the length, formatting and check digit.
@@ -98,7 +100,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Uruguay RUT number."""
     try:
         return bool(validate(number))
@@ -106,7 +108,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return '-'.join([number[:2], number[2:-4], number[-4:-1], number[-1]])
diff --git a/stdnum/vatin.py b/stdnum/vatin.py
index d05e8f7..37b2d3a 100644
--- a/stdnum/vatin.py
+++ b/stdnum/vatin.py
@@ -47,17 +47,19 @@ Traceback (most recent call last):
 InvalidComponent: ...
 """
 
+from __future__ import annotations
+
 import re
 
 from stdnum.exceptions import *
-from stdnum.util import clean, get_cc_module
+from stdnum.util import NumberValidationModule, clean, get_cc_module
 
 
 # Cache of country code modules
 _country_modules = dict()
 
 
-def _get_cc_module(cc):
+def _get_cc_module(cc: str) -> NumberValidationModule:
     """Get the VAT number module based on the country code."""
     # Greece uses a "wrong" country code, special case for Northern Ireland
     cc = cc.lower().replace('el', 'gr').replace('xi', 'gb')
@@ -71,7 +73,7 @@ def _get_cc_module(cc):
     return module
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation."""
     number = clean(number).strip()
     module = _get_cc_module(number[:2])
@@ -81,7 +83,7 @@ def compact(number):
         return module.compact(number)
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid VAT number.
 
     This performs the country-specific check for the number.
@@ -94,7 +96,7 @@ def validate(number):
         return module.validate(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid VAT number."""
     try:
         return bool(validate(number))
diff --git a/stdnum/ve/__init__.py b/stdnum/ve/__init__.py
index 83223b3..4bbca26 100644
--- a/stdnum/ve/__init__.py
+++ b/stdnum/ve/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Venezuelan numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.ve import rif as vat  # noqa: F401
diff --git a/stdnum/ve/rif.py b/stdnum/ve/rif.py
index 2ed3c9f..cd4990a 100644
--- a/stdnum/ve/rif.py
+++ b/stdnum/ve/rif.py
@@ -33,11 +33,13 @@ Traceback (most recent call last):
 InvalidChecksum: ...
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
     return clean(number, ' -').upper().strip()
@@ -54,7 +56,7 @@ _company_types = {
 }
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit for the RIF."""
     number = compact(number)
     weights = (3, 2, 7, 6, 5, 4, 3, 2)
@@ -63,7 +65,7 @@ def calc_check_digit(number):
     return '00987654321'[c % 11]
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided is a valid RIF. This checks the length,
     formatting and check digit."""
     number = compact(number)
@@ -78,7 +80,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided is a valid RIF. This checks the length,
     formatting and check digit."""
     try:
diff --git a/stdnum/verhoeff.py b/stdnum/verhoeff.py
index 401fb1c..9533062 100644
--- a/stdnum/verhoeff.py
+++ b/stdnum/verhoeff.py
@@ -45,6 +45,8 @@ InvalidChecksum: ...
 '12340'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 
 
@@ -74,7 +76,7 @@ _permutation_table = (
     (7, 0, 4, 6, 9, 1, 3, 2, 5, 8))
 
 
-def checksum(number):
+def checksum(number: str) -> int:
     """Calculate the Verhoeff checksum over the provided number. The checksum
     is returned as an int. Valid numbers should have a checksum of 0."""
     check = 0
@@ -83,7 +85,7 @@ def checksum(number):
     return check
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number provided passes the Verhoeff checksum."""
     if not bool(number):
         raise InvalidFormat()
@@ -96,7 +98,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number provided passes the Verhoeff checksum."""
     try:
         return bool(validate(number))
@@ -104,7 +106,7 @@ def is_valid(number):
         return False
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the extra digit that should be appended to the number to
     make it a valid number."""
     return str(_multiplication_table[checksum(str(number) + '0')].index(0))
diff --git a/stdnum/vn/__init__.py b/stdnum/vn/__init__.py
index 62552bc..3374f73 100644
--- a/stdnum/vn/__init__.py
+++ b/stdnum/vn/__init__.py
@@ -20,5 +20,7 @@
 
 """Collection of Vietnam numbers."""
 
+from __future__ import annotations
+
 # provide aliases
 from stdnum.vn import mst as vat  # noqa: F401
diff --git a/stdnum/vn/mst.py b/stdnum/vn/mst.py
index 7e372cf..63c27a9 100644
--- a/stdnum/vn/mst.py
+++ b/stdnum/vn/mst.py
@@ -61,11 +61,13 @@ InvalidChecksum: ...
 '0312687878-001'
 """
 
+from __future__ import annotations
+
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -74,14 +76,14 @@ def compact(number):
     return clean(number, ' -.').strip()
 
 
-def calc_check_digit(number):
+def calc_check_digit(number: str) -> str:
     """Calculate the check digit."""
     weights = (31, 29, 23, 19, 17, 13, 7, 5, 3)
     total = sum(w * int(n) for w, n in zip(weights, number))
     return str(10 - (total % 11))
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid Vietnam MST number.
 
     This checks the length, formatting and check digit.
@@ -100,7 +102,7 @@ def validate(number):
     return number
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid Vietnam MST number."""
     try:
         return bool(validate(number))
@@ -108,7 +110,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return number if len(number) == 10 else '-'.join([number[:10], 
number[10:]])
diff --git a/stdnum/za/idnr.py b/stdnum/za/idnr.py
index 7a8ced1..f323499 100644
--- a/stdnum/za/idnr.py
+++ b/stdnum/za/idnr.py
@@ -49,6 +49,8 @@ datetime.date(1975, 3, 30)
 '750330 5044 08 9'
 """
 
+from __future__ import annotations
+
 import datetime
 
 from stdnum import luhn
@@ -56,7 +58,7 @@ from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -65,7 +67,7 @@ def compact(number):
     return clean(number, ' ')
 
 
-def get_birth_date(number):
+def get_birth_date(number: str) -> datetime.date:
     """Split the date parts from the number and return the date of birth.
 
     Since the number only uses two digits for the year, the century may be
@@ -84,7 +86,7 @@ def get_birth_date(number):
         raise InvalidComponent()
 
 
-def get_gender(number):
+def get_gender(number: str) -> str:
     """Get the gender (M/F) from the person's ID number."""
     number = compact(number)
     if number[6] in '01234':
@@ -93,7 +95,7 @@ def get_gender(number):
         return 'M'
 
 
-def get_citizenship(number):
+def get_citizenship(number: str) -> str:
     """Get the citizenship status (citizen/resident) from the ID number."""
     number = compact(number)
     if number[10] == '0':
@@ -104,7 +106,7 @@ def get_citizenship(number):
         raise InvalidComponent()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid South African ID number.
 
     This checks the length, formatting and check digit.
@@ -119,7 +121,7 @@ def validate(number):
     return luhn.validate(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid South African ID number."""
     try:
         return bool(validate(number))
@@ -127,7 +129,7 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     number = compact(number)
     return ' '.join((number[:6], number[6:10], number[10:12], number[12:]))
diff --git a/stdnum/za/tin.py b/stdnum/za/tin.py
index 9ec400c..62960f9 100644
--- a/stdnum/za/tin.py
+++ b/stdnum/za/tin.py
@@ -43,12 +43,14 @@ InvalidLength: ...
 '0843089848'
 """  # noqa: E501
 
+from __future__ import annotations
+
 from stdnum import luhn
 from stdnum.exceptions import *
 from stdnum.util import clean, isdigits
 
 
-def compact(number):
+def compact(number: str) -> str:
     """Convert the number to the minimal representation.
 
     This strips the number of any valid separators and removes surrounding
@@ -57,7 +59,7 @@ def compact(number):
     return clean(number, ' -/').upper().strip()
 
 
-def validate(number):
+def validate(number: str) -> str:
     """Check if the number is a valid South Africa Tax Reference Number.
 
     This checks the length, formatting and check digit.
@@ -72,7 +74,7 @@ def validate(number):
     return luhn.validate(number)
 
 
-def is_valid(number):
+def is_valid(number: str) -> bool:
     """Check if the number is a valid South Africa Tax Reference Number."""
     try:
         return bool(validate(number))
@@ -80,6 +82,6 @@ def is_valid(number):
         return False
 
 
-def format(number):
+def format(number: str) -> str:
     """Reformat the number to the standard presentation format."""
     return compact(number)
diff --git a/tests/test_by_unp.py b/tests/test_by_unp.py
index 6a375ce..cefbdf2 100644
--- a/tests/test_by_unp.py
+++ b/tests/test_by_unp.py
@@ -35,10 +35,12 @@ from stdnum.by import unp
 class TestNalog(unittest.TestCase):
     """Test the web services provided by the portal.nalog.gov.by web site."""
 
-    def test_check_nalog(self):
+    def test_check_nalog(self) -> None:
         """Test stdnum.by.unp.check_nalog()"""
         # Test a normal valid number
         result = unp.check_nalog('191682495')
+        self.assertTrue(result)
+        assert result
         self.assertDictEqual(
             result,
             {
diff --git a/tests/test_ch_uid.py b/tests/test_ch_uid.py
index 4f73893..ee9063d 100644
--- a/tests/test_ch_uid.py
+++ b/tests/test_ch_uid.py
@@ -36,10 +36,14 @@ class TestUid(unittest.TestCase):
     """Test the UID Webservice provided by the Swiss Federal Statistical
     Office for validating UID numbers."""
 
-    def test_check_uid(self):
+    def test_check_uid(self) -> None:
         """Test stdnum.ch.uid.check_uid()"""
         result = uid.check_uid('CHE113690319')
         self.assertTrue(result)
-        
self.assertEqual(result['organisation']['organisationIdentification']['uid']['uidOrganisationId'],
 113690319)
-        
self.assertEqual(result['organisation']['organisationIdentification']['legalForm'],
 '0220')
-        self.assertEqual(result['vatRegisterInformation']['vatStatus'], '2')
+        self.assertEqual(
+            
result['organisation']['organisationIdentification']['uid']['uidOrganisationId'],
  # type: ignore[index]
+            113690319)
+        self.assertEqual(
+            result['organisation']['organisationIdentification']['legalForm'], 
 # type: ignore[index]
+            '0220')
+        self.assertEqual(result['vatRegisterInformation']['vatStatus'], '2')  
# type: ignore[index]
diff --git a/tests/test_de_handelsregisternummer.py 
b/tests/test_de_handelsregisternummer.py
index 1b0d293..c5354f8 100644
--- a/tests/test_de_handelsregisternummer.py
+++ b/tests/test_de_handelsregisternummer.py
@@ -35,10 +35,12 @@ from stdnum.de import handelsregisternummer
 class TestOffeneRegister(unittest.TestCase):
     """Test the web services provided by the OffeneRegister.de web site."""
 
-    def test_check_offeneregister(self):
+    def test_check_offeneregister(self) -> None:
         """Test stdnum.de.handelsregisternummer.check_offeneregister()"""
         # Test a normal valid number
         result = handelsregisternummer.check_offeneregister('Chemnitz HRB 
32854')
+        self.assertTrue(result)
+        assert result
         self.assertTrue(all(
             key in result.keys()
             for key in ['companyId', 'courtCode', 'courtName', 'name', 
'nativeReferenceNumber']))
diff --git a/tests/test_do_cedula.py b/tests/test_do_cedula.py
index 4141c30..cfaee70 100644
--- a/tests/test_do_cedula.py
+++ b/tests/test_do_cedula.py
@@ -44,10 +44,12 @@ class TestDGII(unittest.TestCase):
     # See https://github.com/arthurdejong/python-stdnum/pull/462
     # and https://github.com/arthurdejong/python-stdnum/issues/461
     @unittest.expectedFailure
-    def test_check_dgii(self):
+    def test_check_dgii(self) -> None:
         """Test stdnum.do.cedula.check_dgii()"""
         # Test a normal valid number
         result = cedula.check_dgii('05500023407')
+        self.assertTrue(result)
+        assert result
         self.assertTrue(all(
             key in result.keys()
             for key in ['cedula', 'name', 'commercial_name', 'category', 
'status']))
@@ -60,4 +62,6 @@ class TestDGII(unittest.TestCase):
         self.assertIsNone(cedula.check_dgii('12345678903'))
         # Test a number on the whitelist
         result = cedula.check_dgii('02300052220')
+        self.assertTrue(result)
+        assert result
         self.assertEqual(result['cedula'], '02300052220')
diff --git a/tests/test_do_ncf.py b/tests/test_do_ncf.py
index dc58b2d..19548dd 100644
--- a/tests/test_do_ncf.py
+++ b/tests/test_do_ncf.py
@@ -36,11 +36,12 @@ class TestDGII(unittest.TestCase):
     """Test the web services provided by the the Dirección General de
     Impuestos Internos (DGII), the Dominican Republic tax department."""
 
-    def test_check_dgii(self):
+    def test_check_dgii(self) -> None:
         """Test stdnum.do.ncf.check_dgii()"""
         # Test a normal valid number
         result = ncf.check_dgii('130546312', 'A010010011500000038')
         self.assertTrue(result)
+        assert result
         self.assertIn('name', result.keys())
         self.assertIn('rnc', result.keys())
         self.assertIn('ncf', result.keys())
@@ -58,6 +59,7 @@ class TestDGII(unittest.TestCase):
         # Test the new format
         result = ncf.check_dgii('130546312', 'B0100000005')
         self.assertTrue(result)
+        assert result
         self.assertIn('name', result.keys())
         self.assertIn('rnc', result.keys())
         self.assertIn('ncf', result.keys())
@@ -68,6 +70,7 @@ class TestDGII(unittest.TestCase):
         result = ncf.check_dgii('101010632', 'E310049533639',
                                 buyer_rnc='22400559690', 
security_code='hnI63Q')
         self.assertTrue(result)
+        assert result
         self.assertIn('status', result.keys())
         self.assertEqual(result['issuing_rnc'], '101010632')
         self.assertEqual(result['buyer_rnc'], '22400559690')
diff --git a/tests/test_do_rnc.py b/tests/test_do_rnc.py
index 5c1531b..f8c9f4f 100644
--- a/tests/test_do_rnc.py
+++ b/tests/test_do_rnc.py
@@ -44,10 +44,12 @@ class TestDGII(unittest.TestCase):
     # See https://github.com/arthurdejong/python-stdnum/pull/462
     # and https://github.com/arthurdejong/python-stdnum/issues/461
     @unittest.expectedFailure
-    def test_check_dgii(self):
+    def test_check_dgii(self) -> None:
         """Test stdnum.do.rnc.check_dgii()"""
         # Test a normal valid number
         result = rnc.check_dgii('131098193')
+        self.assertTrue(result)
+        assert result
         self.assertTrue(all(
             key in result.keys()
             for key in ['rnc', 'name', 'commercial_name', 'category', 
'status']))
@@ -60,10 +62,14 @@ class TestDGII(unittest.TestCase):
         self.assertIsNone(rnc.check_dgii('814387152'))
         # Test a number on the whitelist
         result = rnc.check_dgii('501658167')
+        self.assertTrue(result)
+        assert result
         self.assertEqual(result['rnc'], '501658167')
         # Test the output unescaping (\t and \n) of the result so JSON
         # deserialisation works
         result = rnc.check_dgii('132070801')
+        self.assertTrue(result)
+        assert result
         self.assertEqual(result['rnc'], '132070801')
 
     # Theses tests currently fail because the SOAP service at
@@ -74,7 +80,7 @@ class TestDGII(unittest.TestCase):
     # See https://github.com/arthurdejong/python-stdnum/pull/462
     # and https://github.com/arthurdejong/python-stdnum/issues/461
     @unittest.expectedFailure
-    def test_search_dgii(self):
+    def test_search_dgii(self) -> None:
         """Test stdnum.do.rnc.search_dgii()"""
         # Search for some existing companies
         results = rnc.search_dgii('EXPORT DE')
diff --git a/tests/test_eu_vat.py b/tests/test_eu_vat.py
index 84eebf4..d0b4e21 100644
--- a/tests/test_eu_vat.py
+++ b/tests/test_eu_vat.py
@@ -36,14 +36,14 @@ class TestVies(unittest.TestCase):
     """Test the VIES web service provided by the European commission for
     validation VAT numbers of European countries."""
 
-    def test_check_vies(self):
+    def test_check_vies(self) -> None:
         """Test stdnum.eu.vat.check_vies()"""
         result = vat.check_vies('NL4495445B01')
         self.assertTrue(result['valid'])
         self.assertEqual(result['countryCode'], 'NL')
         self.assertEqual(result['vatNumber'], '004495445B01')
 
-    def test_check_vies_approx(self):
+    def test_check_vies_approx(self) -> None:
         """Test stdnum.eu.vat.check_vies_approx()"""
         result = vat.check_vies_approx('NL4495445B01', 'NL4495445B01')
         self.assertTrue(result['valid'])
diff --git a/tox.ini b/tox.ini
index 23a313b..400beb0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = py{38,39,310,311,312,313,py3},flake8,docs,headers
+envlist = py{38,39,310,311,312,313,py3},flake8,mypy,docs,headers
 skip_missing_interpreters = true
 
 [testenv]
@@ -30,6 +30,19 @@ commands = flake8 .
 setenv=
     PYTHONWARNINGS=ignore
 
+[testenv:mypy]
+skip_install = true
+deps = mypy
+       types-requests
+       zeep
+commands =
+    mypy tests
+    mypy -p stdnum --python-version 3.9
+    mypy -p stdnum --python-version 3.10
+    mypy -p stdnum --python-version 3.11
+    mypy -p stdnum --python-version 3.12
+    mypy -p stdnum --python-version 3.13
+
 [testenv:docs]
 use_develop = true
 deps = Sphinx

-----------------------------------------------------------------------

Summary of changes:
 .github/workflows/test.yml             |  2 +-
 .gitignore                             |  3 ++
 docs/index.rst                         | 12 ++---
 setup.cfg                              |  8 ++++
 setup.py                               |  2 +-
 stdnum/__init__.py                     |  2 +
 stdnum/ad/__init__.py                  |  2 +
 stdnum/ad/nrt.py                       | 10 ++--
 stdnum/al/__init__.py                  |  2 +
 stdnum/al/nipt.py                      |  8 ++--
 stdnum/ar/__init__.py                  |  2 +
 stdnum/ar/cbu.py                       | 12 +++--
 stdnum/ar/cuit.py                      | 12 +++--
 stdnum/ar/dni.py                       | 10 ++--
 stdnum/at/__init__.py                  |  2 +
 stdnum/at/businessid.py                |  8 ++--
 stdnum/at/postleitzahl.py              | 10 ++--
 stdnum/at/tin.py                       | 18 ++++----
 stdnum/at/uid.py                       | 10 ++--
 stdnum/at/vnr.py                       | 10 ++--
 stdnum/au/__init__.py                  |  2 +
 stdnum/au/abn.py                       | 12 +++--
 stdnum/au/acn.py                       | 14 +++---
 stdnum/au/tfn.py                       | 12 +++--
 stdnum/be/__init__.py                  |  2 +
 stdnum/be/bis.py                       | 20 ++++----
 stdnum/be/eid.py                       | 12 +++--
 stdnum/be/iban.py                      | 12 +++--
 stdnum/be/nn.py                        | 22 +++++----
 stdnum/be/ssn.py                       | 24 ++++++----
 stdnum/be/vat.py                       | 10 ++--
 stdnum/bg/egn.py                       | 12 +++--
 stdnum/bg/pnf.py                       | 10 ++--
 stdnum/bg/vat.py                       | 12 +++--
 stdnum/bic.py                          | 10 ++--
 stdnum/bitcoin.py                      | 21 +++++----
 stdnum/br/__init__.py                  |  3 ++
 stdnum/br/cnpj.py                      | 12 +++--
 stdnum/br/cpf.py                       | 12 +++--
 stdnum/by/__init__.py                  |  2 +
 stdnum/by/unp.py                       | 19 +++++---
 stdnum/ca/__init__.py                  |  3 ++
 stdnum/ca/bc_phn.py                    | 11 +++--
 stdnum/ca/bn.py                        |  8 ++--
 stdnum/ca/sin.py                       | 10 ++--
 stdnum/casrn.py                        | 10 ++--
 stdnum/cfi.py                          | 10 ++--
 stdnum/ch/esr.py                       | 12 +++--
 stdnum/ch/ssn.py                       | 10 ++--
 stdnum/ch/uid.py                       | 29 ++++++++----
 stdnum/ch/vat.py                       | 10 ++--
 stdnum/cl/__init__.py                  |  3 ++
 stdnum/cl/rut.py                       | 12 +++--
 stdnum/cn/__init__.py                  |  2 +
 stdnum/cn/ric.py                       | 16 ++++---
 stdnum/cn/uscc.py                      | 12 +++--
 stdnum/co/__init__.py                  |  3 ++
 stdnum/co/nit.py                       | 12 +++--
 stdnum/cr/__init__.py                  |  3 ++
 stdnum/cr/cpf.py                       | 10 ++--
 stdnum/cr/cpj.py                       | 10 ++--
 stdnum/cr/cr.py                        | 10 ++--
 stdnum/cu/ni.py                        | 12 +++--
 stdnum/cusip.py                        | 12 +++--
 stdnum/cy/vat.py                       | 10 ++--
 stdnum/cz/__init__.py                  |  2 +
 stdnum/cz/bankaccount.py               | 22 +++++----
 stdnum/cz/dic.py                       | 12 +++--
 stdnum/cz/rc.py                        | 12 +++--
 stdnum/damm.py                         | 18 ++++++--
 stdnum/de/__init__.py                  |  2 +
 stdnum/de/handelsregisternummer.py     | 24 +++++++---
 stdnum/de/idnr.py                      | 12 +++--
 stdnum/de/stnr.py                      | 45 ++++++++++--------
 stdnum/de/vat.py                       |  8 ++--
 stdnum/de/wkn.py                       | 10 ++--
 stdnum/dk/__init__.py                  |  2 +
 stdnum/dk/cpr.py                       | 14 +++---
 stdnum/dk/cvr.py                       | 10 ++--
 stdnum/do/__init__.py                  |  2 +
 stdnum/do/cedula.py                    | 16 +++++--
 stdnum/do/ncf.py                       | 21 ++++++---
 stdnum/do/rnc.py                       | 28 ++++++++----
 stdnum/dz/__init__.py                  |  2 +
 stdnum/dz/nif.py                       | 10 ++--
 stdnum/ean.py                          | 10 ++--
 stdnum/ec/__init__.py                  |  2 +
 stdnum/ec/ci.py                        | 13 ++++--
 stdnum/ec/ruc.py                       | 18 ++++----
 stdnum/ee/__init__.py                  |  2 +
 stdnum/ee/ik.py                        | 14 +++---
 stdnum/ee/kmkr.py                      | 10 ++--
 stdnum/ee/registrikood.py              |  8 ++--
 stdnum/eg/__init__.py                  |  2 +
 stdnum/eg/tn.py                        | 10 ++--
 stdnum/es/__init__.py                  |  2 +
 stdnum/es/cae.py                       |  8 ++--
 stdnum/es/ccc.py                       | 16 ++++---
 stdnum/es/cif.py                       | 10 ++--
 stdnum/es/cups.py                      | 12 +++--
 stdnum/es/dni.py                       | 10 ++--
 stdnum/es/iban.py                      |  8 ++--
 stdnum/es/nie.py                       |  8 ++--
 stdnum/es/nif.py                       |  8 ++--
 stdnum/es/postal_code.py               |  7 +--
 stdnum/es/referenciacatastral.py       | 14 +++---
 stdnum/eu/at_02.py                     | 12 +++--
 stdnum/eu/banknote.py                  | 10 ++--
 stdnum/eu/ecnumber.py                  | 10 ++--
 stdnum/eu/eic.py                       | 10 ++--
 stdnum/eu/nace.py                      | 16 ++++---
 stdnum/eu/oss.py                       |  7 +--
 stdnum/eu/vat.py                       | 36 ++++++++++-----
 stdnum/exceptions.py                   |  4 +-
 stdnum/fi/__init__.py                  |  2 +
 stdnum/fi/alv.py                       | 10 ++--
 stdnum/fi/associationid.py             | 10 ++--
 stdnum/fi/hetu.py                      | 10 ++--
 stdnum/fi/veronumero.py                |  8 ++--
 stdnum/fi/ytunnus.py                   | 10 ++--
 stdnum/figi.py                         | 10 ++--
 stdnum/fo/__init__.py                  |  2 +
 stdnum/fo/vn.py                        | 10 ++--
 stdnum/fr/__init__.py                  |  2 +
 stdnum/fr/nif.py                       | 12 +++--
 stdnum/fr/nir.py                       | 12 +++--
 stdnum/fr/siren.py                     | 10 ++--
 stdnum/fr/siret.py                     | 14 +++---
 stdnum/fr/tva.py                       |  8 ++--
 stdnum/gb/nhs.py                       | 12 +++--
 stdnum/gb/sedol.py                     | 12 +++--
 stdnum/gb/upn.py                       | 10 ++--
 stdnum/gb/utr.py                       | 10 ++--
 stdnum/gb/vat.py                       | 12 +++--
 stdnum/gh/__init__.py                  |  2 +
 stdnum/gh/tin.py                       | 10 ++--
 stdnum/gn/__init__.py                  |  2 +
 stdnum/gn/nifp.py                      | 10 ++--
 stdnum/gr/amka.py                      | 12 +++--
 stdnum/gr/vat.py                       | 10 ++--
 stdnum/grid.py                         | 14 +++---
 stdnum/gs1_128.py                      | 32 +++++++++----
 stdnum/gt/__init__.py                  |  2 +
 stdnum/gt/nit.py                       | 12 +++--
 stdnum/hr/__init__.py                  |  2 +
 stdnum/hr/oib.py                       |  8 ++--
 stdnum/hu/__init__.py                  |  2 +
 stdnum/hu/anum.py                      | 10 ++--
 stdnum/iban.py                         | 20 ++++----
 stdnum/id/__init__.py                  |  2 +
 stdnum/id/nik.py                       | 12 +++--
 stdnum/id/npwp.py                      | 10 ++--
 stdnum/ie/pps.py                       |  8 ++--
 stdnum/ie/vat.py                       | 12 +++--
 stdnum/il/__init__.py                  |  2 +
 stdnum/il/hp.py                        | 10 ++--
 stdnum/il/idnr.py                      | 10 ++--
 stdnum/imei.py                         | 18 ++++----
 stdnum/imo.py                          | 12 +++--
 stdnum/imsi.py                         | 12 +++--
 stdnum/in_/__init__.py                 |  2 +
 stdnum/in_/aadhaar.py                  | 12 +++--
 stdnum/in_/epic.py                     |  8 ++--
 stdnum/in_/gstin.py                    | 12 +++--
 stdnum/in_/pan.py                      | 12 +++--
 stdnum/in_/vid.py                      | 12 +++--
 stdnum/is_/__init__.py                 |  2 +
 stdnum/is_/kennitala.py                | 12 +++--
 stdnum/is_/vsk.py                      |  8 ++--
 stdnum/isan.py                         | 39 +++++++++-------
 stdnum/isbn.py                         | 20 ++++----
 stdnum/isil.py                         | 12 +++--
 stdnum/isin.py                         | 12 +++--
 stdnum/ismn.py                         | 17 ++++---
 stdnum/isni.py                         |  9 ++--
 stdnum/iso11649.py                     | 10 ++--
 stdnum/iso6346.py                      | 12 +++--
 stdnum/iso7064/mod_11_10.py            | 10 ++--
 stdnum/iso7064/mod_11_2.py             | 10 ++--
 stdnum/iso7064/mod_37_2.py             | 10 ++--
 stdnum/iso7064/mod_37_36.py            | 10 ++--
 stdnum/iso7064/mod_97_10.py            | 12 +++--
 stdnum/isrc.py                         | 10 ++--
 stdnum/issn.py                         | 14 +++---
 stdnum/it/__init__.py                  |  2 +
 stdnum/it/aic.py                       | 18 ++++----
 stdnum/it/codicefiscale.py             | 14 +++---
 stdnum/it/iva.py                       |  8 ++--
 stdnum/jp/__init__.py                  |  3 ++
 stdnum/jp/cn.py                        | 12 +++--
 stdnum/ke/__init__.py                  |  2 +
 stdnum/ke/pin.py                       | 10 ++--
 stdnum/kr/__init__.py                  |  2 +
 stdnum/kr/brn.py                       | 10 ++--
 stdnum/kr/rrn.py                       | 14 +++---
 stdnum/lei.py                          |  8 ++--
 stdnum/li/peid.py                      |  8 ++--
 stdnum/lt/__init__.py                  |  2 +
 stdnum/lt/asmens.py                    |  8 ++--
 stdnum/lt/pvm.py                       | 10 ++--
 stdnum/lu/__init__.py                  |  2 +
 stdnum/lu/tva.py                       | 10 ++--
 stdnum/luhn.py                         | 16 ++++---
 stdnum/lv/__init__.py                  |  2 +
 stdnum/lv/pvn.py                       | 14 +++---
 stdnum/ma/__init__.py                  |  2 +
 stdnum/ma/ice.py                       | 10 ++--
 stdnum/mac.py                          | 28 ++++++------
 stdnum/mc/__init__.py                  |  2 +
 stdnum/mc/tva.py                       |  8 ++--
 stdnum/md/idno.py                      | 10 ++--
 stdnum/me/__init__.py                  |  2 +
 stdnum/me/iban.py                      |  8 ++--
 stdnum/me/pib.py                       | 12 +++--
 stdnum/meid.py                         | 35 ++++++++------
 stdnum/mk/__init__.py                  |  2 +
 stdnum/mk/edb.py                       | 12 +++--
 stdnum/mt/vat.py                       | 10 ++--
 stdnum/mu/nid.py                       | 12 +++--
 stdnum/mx/__init__.py                  |  2 +
 stdnum/mx/curp.py                      | 14 +++---
 stdnum/mx/rfc.py                       | 14 +++---
 stdnum/my/nric.py                      | 14 +++---
 stdnum/nl/__init__.py                  |  2 +
 stdnum/nl/brin.py                      |  8 ++--
 stdnum/nl/bsn.py                       | 12 +++--
 stdnum/nl/btw.py                       |  8 ++--
 stdnum/nl/identiteitskaartnummer.py    |  8 ++--
 stdnum/nl/onderwijsnummer.py           |  6 ++-
 stdnum/nl/postcode.py                  |  8 ++--
 stdnum/no/__init__.py                  |  2 +
 stdnum/no/fodselsnummer.py             | 18 ++++----
 stdnum/no/iban.py                      |  8 ++--
 stdnum/no/kontonr.py                   | 14 +++---
 stdnum/no/mva.py                       | 10 ++--
 stdnum/no/orgnr.py                     | 12 +++--
 stdnum/numdb.py                        | 42 +++++++++++------
 stdnum/nz/__init__.py                  |  2 +
 stdnum/nz/bankaccount.py               | 22 +++++----
 stdnum/nz/ird.py                       | 12 +++--
 stdnum/pe/__init__.py                  |  3 ++
 stdnum/pe/cui.py                       | 12 +++--
 stdnum/pe/ruc.py                       | 12 +++--
 stdnum/pk/cnic.py                      | 14 +++---
 stdnum/pl/__init__.py                  |  2 +
 stdnum/pl/nip.py                       | 12 +++--
 stdnum/pl/pesel.py                     | 14 +++---
 stdnum/pl/regon.py                     | 14 +++---
 stdnum/pt/__init__.py                  |  2 +
 stdnum/pt/cc.py                        | 15 +++---
 stdnum/pt/nif.py                       | 10 ++--
 stdnum/py.typed                        |  0
 stdnum/py/__init__.py                  |  2 +
 stdnum/py/ruc.py                       | 12 +++--
 stdnum/ro/__init__.py                  |  2 +
 stdnum/ro/cf.py                        |  8 ++--
 stdnum/ro/cnp.py                       | 14 +++---
 stdnum/ro/cui.py                       | 10 ++--
 stdnum/ro/onrc.py                      |  8 ++--
 stdnum/rs/__init__.py                  |  2 +
 stdnum/rs/pib.py                       |  8 ++--
 stdnum/ru/__init__.py                  |  2 +
 stdnum/ru/inn.py                       | 16 ++++---
 stdnum/ru/ogrn.py                      | 10 ++--
 stdnum/se/__init__.py                  |  2 +
 stdnum/se/orgnr.py                     | 10 ++--
 stdnum/se/personnummer.py              | 14 +++---
 stdnum/se/postnummer.py                | 10 ++--
 stdnum/se/vat.py                       |  8 ++--
 stdnum/sg/__init__.py                  |  2 +
 stdnum/sg/uen.py                       | 22 +++++----
 stdnum/si/__init__.py                  |  2 +
 stdnum/si/ddv.py                       | 10 ++--
 stdnum/si/emso.py                      | 18 ++++----
 stdnum/si/maticna.py                   | 10 ++--
 stdnum/sk/__init__.py                  |  2 +
 stdnum/sk/dph.py                       | 10 ++--
 stdnum/sk/rc.py                        |  3 ++
 stdnum/sm/__init__.py                  |  2 +
 stdnum/sm/coe.py                       |  8 ++--
 stdnum/sv/__init__.py                  |  2 +
 stdnum/sv/nit.py                       | 12 +++--
 stdnum/th/moa.py                       | 10 ++--
 stdnum/th/pin.py                       | 12 +++--
 stdnum/th/tin.py                       | 16 ++++---
 stdnum/tn/__init__.py                  |  2 +
 stdnum/tn/mf.py                        | 10 ++--
 stdnum/tr/__init__.py                  |  3 ++
 stdnum/tr/tckimlik.py                  | 23 +++++++---
 stdnum/tr/vkn.py                       | 10 ++--
 stdnum/tw/__init__.py                  |  2 +
 stdnum/tw/ubn.py                       | 12 +++--
 stdnum/ua/edrpou.py                    | 18 ++++----
 stdnum/ua/rntrc.py                     | 12 +++--
 stdnum/us/atin.py                      | 10 ++--
 stdnum/us/ein.py                       | 12 +++--
 stdnum/us/itin.py                      | 10 ++--
 stdnum/us/ptin.py                      |  8 ++--
 stdnum/us/rtn.py                       | 10 ++--
 stdnum/us/ssn.py                       | 10 ++--
 stdnum/us/tin.py                       | 16 ++++---
 stdnum/util.py                         | 84 ++++++++++++++++++++++++++--------
 stdnum/uy/__init__.py                  |  2 +
 stdnum/uy/rut.py                       | 12 +++--
 stdnum/vatin.py                        | 12 +++--
 stdnum/ve/__init__.py                  |  2 +
 stdnum/ve/rif.py                       | 10 ++--
 stdnum/verhoeff.py                     | 10 ++--
 stdnum/vn/__init__.py                  |  2 +
 stdnum/vn/mst.py                       | 12 +++--
 stdnum/za/idnr.py                      | 16 ++++---
 stdnum/za/tin.py                       | 10 ++--
 tests/test_by_unp.py                   |  4 +-
 tests/test_ch_uid.py                   | 12 +++--
 tests/test_de_handelsregisternummer.py |  4 +-
 tests/test_do_cedula.py                |  6 ++-
 tests/test_do_ncf.py                   |  5 +-
 tests/test_do_rnc.py                   | 10 +++-
 tests/test_eu_vat.py                   |  4 +-
 tox.ini                                | 15 +++++-
 320 files changed, 2058 insertions(+), 1235 deletions(-)
 create mode 100644 stdnum/py.typed


hooks/post-receive
-- 
python-stdnum