Type variables with value restriction

By default, a type variable can be replaced with any type. However, sometimesit’s useful to have a type variable that can only have some specific typesas its value. A typical example is a type variable that can only have valuesstr and bytes:

  1. from typing import TypeVar
  2.  
  3. AnyStr = TypeVar('AnyStr', str, bytes)

This is actually such a common type variable that AnyStr isdefined in typing and we don’t need to define it ourselves.

We can use AnyStr to define a function that can concatenatetwo strings or bytes objects, but it can’t be called with otherargument types:

  1. from typing import AnyStr
  2.  
  3. def concat(x: AnyStr, y: AnyStr) -> AnyStr:
  4. return x + y
  5.  
  6. concat('a', 'b') # Okay
  7. concat(b'a', b'b') # Okay
  8. concat(1, 2) # Error!

Note that this is different from a union type, since combinationsof str and bytes are not accepted:

  1. concat('string', b'bytes') # Error!

In this case, this is exactly what we want, since it’s not possibleto concatenate a string and a bytes object! The type checkerwill reject this function:

  1. def union_concat(x: Union[str, bytes], y: Union[str, bytes]) -> Union[str, bytes]:
  2. return x + y # Error: can't concatenate str and bytes

Another interesting special case is calling concat() with asubtype of str:

  1. class S(str): pass
  2.  
  3. ss = concat(S('foo'), S('bar'))

You may expect that the type of ss is S, but the type isactually str: a subtype gets promoted to one of the valid valuesfor the type variable, which in this case is str. This is thussubtly different from bounded quantification in languages such asJava, where the return type would be S. The way mypy implementsthis is correct for concat, since concat actually returns astr instance in the above example:

  1. >>> print(type(ss))
  2. <class 'str'>

You can also use a TypeVar with a restricted set of possiblevalues when defining a generic class. For example, mypy uses the typePattern[AnyStr] for the return value of re.compile(),since regular expressions can be based on a string or a bytes pattern.