Typevars are useful if you want to annotate generic classes and function. In your case, you don't need it. Answer from LongerHV on reddit.com
🌐
Python documentation
docs.python.org › 3 › library › typing.html
typing — Support for type hints
from collections.abc import Sequence from typing import TypeVar U = TypeVar('U') # Declare type variable "U" def second(l: Sequence[U]) -> U: # Function is generic over the TypeVar "U" return l[1] Changed in version 3.12: Syntactic support for generics is new in Python 3.12. For most containers in Python, the typing system assumes that all elements in the container will be of the same type. For example:
🌐
Mypy
mypy.readthedocs.io › en › stable › generics.html
Generics - mypy 1.19.1 documentation
Here is the same example using the old syntax (required for Python 3.11 and earlier, but also supported on newer Python versions): from typing import TypeVar, Generic T = TypeVar('T') # Define type variable "T" class Stack(Generic[T]): def __init__(self) -> None: # Create an empty list with items of type T self.items: list[T] = [] def push(self, item: T) -> None: self.items.append(item) def pop(self) -> T: return self.items.pop() def empty(self) -> bool: return not self.items
🌐
ProgramCreek
programcreek.com › python › example › 94082 › typing.TypeVar
Python Examples of typing.TypeVar
def set_max_num_iterations(self, max_num_iterations: int) -> TypeVar("Trainer"): r"""Set maximum number of training iterations to the trainer Args: max_num_iterations (int): maximum number of training iterations. Raises: TypeError: when type of max_num_iterations is not allowed.
🌐
Reddit
reddit.com › r/learnpython › should i use a typevar?
r/learnpython on Reddit: Should I use a TypeVar?
January 28, 2024 -

In my code I have an alias for a type and mypy is happy about it.

NumberedPaths = list[tuple[str, str]]

Should I use TypeVar instead? Is there any better way to define a new type?

🌐
Medium
gabrielgomes61320.medium.com › python-development-best-practices-using-typevar-1b88db0c1473
Python Development Best Practices — Using TypeVar | by Gabriel Gomes, PhD | Medium
February 18, 2024 - The Python Azure SDK, for instance, is an example of how type annotations can help us understand code faster and also in the process of debugging code, in some cases. Although Python is not a statically typed programming language, those type annotations can guide us to spot places in which we may be using wrong types, thus leading to weird behavior in functions we used throughout our code. This post will be relatively simple and fast to read, and will focus on how we can use TypeVar, a feature of the typing Python library, to make type annotations a little bit more “dynamic”.
Top answer
1 of 2
222

The two concepts aren't related any more than any other type-related concepts.

In short, a TypeVar is a variable you can use in type signatures so you can refer to the same unspecified type more than once, while a NewType is used to tell the type checker that some values should be treated as their own type.

Type Variables

To simplify, type variables let you refer to the same type more than once without specifying exactly which type it is.

In a definition, a single type variable always takes the same value.

# (This code will type check, but it won't run.)
from typing import TypeVar, Generic

# Two type variables, named T and R
T = TypeVar('T')
R = TypeVar('R')

# Put in a list of Ts and get out one T
def get_one(x: list[T]) -> T: ...

# Put in a T and an R, get back an R and a T
def swap(x: T, y: R) -> tuple[R, T]:
    return y, x

# A simple generic class that holds a value of type T
class ValueHolder(Generic[T]):
    def __init__(self, value: T):
        self.value = value
    def get(self) -> T:
        return self.value

x: ValueHolder[int] = ValueHolder(123)
y: ValueHolder[str] = ValueHolder('abc')

Without type variables, there wouldn't be a good way to declare the type of get_one or ValueHolder.get.

There are a few other options on TypeVar. You can restrict the possible values by passing in more types (e.g. TypeVar(name, int, str)), or you can give an upper bound so every value of the type variable must be a subtype of that type (e.g. TypeVar(name, bound=int)).

Additionally, you can decide whether a type variable is covariant, contravariant, or neither when you declare it. This essentially decides when subclasses or superclasses can be used in place of a generic type. PEP 484 describes these concepts in more detail, and refers to additional resources.

Addendum: Python 3.12 generic parameter lists

Starting in Python 3.12, the following syntax has been available to declare type variables.

def get_oneT -> T: ...

def swapT, R -> tuple[R, T]: ...

class ValueHolder[T]:
    def __init__(self, value: T): ...
    def get(self) -> T: ...

These declarations are equivalent to those above, but now the type variables are only defined in type signatures within their functions/classes, rather than being stored in regular Python variables. The Python 3.12 release notes contain a summary, as well as links to more-detailed documentation.

NewType

A NewType is for when you want to declare a distinct type without actually doing the work of creating a new type or worry about the overhead of creating new class instances.

In the type checker, NewType('Name', int) creates a subclass of int named "Name."

At runtime, NewType('Name', int) is not a class at all; it is actually the identity function, so x is NewType('Name', int)(x) is always true.

from typing import NewType

UserId = NewType('UserId', int)

def get_user(x: UserId): ...

get_user(UserId(123456)) # this is fine
get_user(123456) # that's an int, not a UserId

UserId(123456) + 123456 # fine, because UserId is a subclass of int

To the type checker, UserId looks something like this:

class UserId(int): pass

But at runtime, UserId is basically just this:

def UserId(x): return x

There's almost nothing more than that to a NewType at runtime. In Python 3.8.1, its implementation was almost exactly as follows:

def NewType(name, type_):
    def identity(x):
        return x
    identity.__name__ = name
    return identity
2 of 2
0

NewType() accepts an unique type parameter. To specialize the function for different types for static typing, you only need a TypeVar here.

Example: Read https://dev.to/decorator_factory/typevars-explained-hmo

🌐
Medium
medium.com › pythoneers › understanding-typevar-in-python-f78e5108471d
Understanding TypeVar in Python. A Quick Guide to Generics, Best… | by Harshit Singh | The Pythoneers | Medium
January 8, 2025 - Modern Python embraces type hints as a way to make code more readable and maintainable. Among these, TypeVar is a powerful tool that helps developers create flexible and reusable code through generics. In this article, we’ll explore what TypeVar is, how it’s used, its relationship with generics, and why it’s not inheritance.
🌐
Snyk
snyk.io › advisor › typing › functions › typing.typevar
How to use the typing.TypeVar function in typing | Snyk
import typing import traitlets T1 = typing.TypeVar('T1') T2 = typing.TypeVar('T2') T3 = typing.TypeVar('T3') T4 = typing.TypeVar('T4') T = typing.TypeVar('T') K = typing.TypeVar('K') V = typing.TypeVar('V') # Define wrappers for traitlets classes. These simply provide Python type hints # that correspond to actual instance type that will result after a class is # instantiated (e.g.
Find elsewhere
🌐
Piccolo-orm
piccolo-orm.com › blog › advanced-type-annotations-using-python-s-type-var
Advanced type annotations using Python's TypeVar - Piccolo Blog
import decimal from typing import TypeVar Number = TypeVar("Number", int, float, decimal.Decimal) def double(value: Number) -> Number: return value * 2
🌐
Rogulski
rogulski.it › blog › python-typing-with-generic-typevar
Python type annotation improvement with Generic and TypeVar | rogulski.it
October 24, 2021 - In this article, I would like to share my favorite way of type annotating classes using typing modules with Generic and TypeVar. First, to check python typing you can use mypy library or any IDE/editor with type checker like Pycharm.
🌐
Medium
medium.com › @mcsavvy › python-type-hints-typevars-what-the-hell-are-they-dbbf63fbfaad
Python type hints: TypeVars- what the hell are they? | by David John (Chukwuemeka) | Medium
September 14, 2021 - Since we are assumming that the keys would be strings, we are also assuming that the function would return either None, a str or a list of strings List[str]. Python had anticipated this problem so they provide TypeVar it for us.
🌐
HotExamples
python.hotexamples.com › examples › typing › - › typevar › python-typevar-function-examples.html
Python typevar Examples, typing.typevar Python Examples - HotExamples
# Stubs for builtins (Python 2.7) from typing import (Undefined, typevar, AbstractGeneric, Iterator, Iterable, overload, Sequence, Mapping, Tuple, List, Any, Dict, Function, Generic, Set, AbstractSet, Sized, Reversible, SupportsInt, SupportsFloat, SupportsAbs, SupportsRound, IO, BinaryIO, builtinclass, ducktype, Union, AnyStr) from abc import abstractmethod, ABCMeta _T = typevar('_T') _KT = typevar('_KT') _VT = typevar('_VT') _S = typevar('_S') _T1 = typevar('_T1') _T2 = typevar('_T2') _T3 = typevar('_T3') _T4 = typevar('_T4') staticmethod = object() # Only valid as a decorator.
🌐
GitHub
github.com › python › typing › issues › 39
TypeVar example in PEP is confusing · Issue #39 · python/typing
January 15, 2015 - The following type variable constraint example from the PEP is confusing: from typing import Iterable X = TypeVar('X') Y = TypeVar('Y', Iterable[X]) def filter(rule: Callable[[X], bool], input: Y) -> Y: ...
Author   JukkaL
🌐
Python
typing.python.org › en › latest › reference › generics.html
Generics — typing documentation
User-defined generic classes and generic classes defined in typing can be used as a base class for another class (generic or non-generic). For example: from typing import Generic, TypeVar, Mapping, Iterator KT = TypeVar('KT') VT = TypeVar('VT') # This is a generic subclass of Mapping class ...
🌐
Python.org
discuss.python.org › typing
How to write a function that accepts either a TypeVar or a list of that TypeVar? - Typing - Discussions on Python.org
January 15, 2024 - V = TypeVar("V") # if we had type negation, would be bound to !list def baz(val: Union[list[V], V]) -> V: if isinstance(val, list): return val[0] return val baz(1) baz([1]) mypy & pyright both point out that, in baz([1]), debugme.py:7: not...
🌐
OneUptime
oneuptime.com › home › blog › how to build generic types with typevar in python
How to Build Generic Types with TypeVar in Python
January 30, 2026 - Here's a configuration and examples: [mypy] python_version = 3.11 strict = true warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true disallow_incomplete_defs = true check_untyped_defs = true disallow_untyped_decorators = true no_implicit_optional = true warn_redundant_casts = true warn_unused_ignores = true warn_no_return = true ... from typing import TypeVar, Generic, List T = TypeVar('T') # Error: Type variable "T" is unbound # def bad_function() -> T: # Wrong!
🌐
Python.org
discuss.python.org › python help
Generics, Type[T], and TypeVarTuple - Python Help - Discussions on Python.org
September 4, 2023 - With non-variadic generics, I can ... from typing import Generic, Type, TypeVar T = TypeVar("T") class Example(Generic[T]): atype: Type[T] def __init__(self, atype: Type[T]): self.atype = atype def get_value(self) -> T: # I can use ...
🌐
Real Python
realpython.com › python-type-checking
Python Type Checking (Guide) – Real Python
July 15, 2024 - 1# choose.py 2 3import random 4from typing import Sequence, TypeVar 5 6Choosable = TypeVar("Choosable", str, float) 7 8def choose(items: Sequence[Choosable]) -> Choosable: 9 return random.choice(items) 10 11reveal_type(choose(["Guido", "Jukka", "Ivan"])) 12reveal_type(choose([1, 2, 3])) 13reveal_type(choose([True, 42, 3.14])) 14reveal_type(choose(["Python", 3, 7])) Now Choosable can only be either str or float, and mypy will note that the last example is an error:
🌐
Python
docs.python.org › 3.10 › library › typing.html
typing — Support for type hints — Python 3.10.19 documentation
For example, to add basic logging to a function, one can create a decorator add_logging to log function calls. The parameter specification variable tells the type checker that the callable passed into the decorator and the new callable returned by it have inter-dependent type parameters: from ...
Top answer
1 of 3
9

The purpose of the TypeVar in this context is to say that the function returns a specific type that is related to the argument's type.

For example, if you did:

a = first([1, 2, 3]) + "foo"

you would get an error, because in this expression T becomes bound to the type int, and so you'd get an error about adding an int and a str.

If you annotated first with Any types as you describe, this would not produce a mypy error (and hence you'd get a TypeError at runtime instead), because the return value of first would always simply be Any.

See the mypy documentation on generics for a lot more examples of how to use typevars: https://mypy.readthedocs.io/en/stable/generics.html

2 of 3
1

The best example is with pydantic.

Imagine I have a function that implements pydantic, and I want that function to be able to handle my pydantic type for records retrieved as a dict from firestore. That code may look something like this:

class MyModel(BaseModel):
    ...

class MyClass:
    def get_records(...) -> Generator[MyModel, None, None]:
        for record in self.client.collection("MyModel").where(...).stream(...):
            body = record.to_dict()
            if body:
                yield MyModel.model_validate(body)

Now that's great and all, but then what if I have multiple models, then I have to define a function for each one, right? Pretty annoying.

Ok what if I use a Union.

class MyModel(BaseModel):
    ...

class MyModel2(BaseModel):
    ...

T: TypeAlias = Union[MyModel, MyModel2]

class MyClass:
    def get_records(
        my_union_type: Type[T],
        collection_name: str
    ) -> Generator[T, None, None]:
        for record in self.client.collection(collection_name).where(...).stream(...):
            body = record.to_dict()
            if body:
                if isinstance(my_union_type, MyModel):
                    yield MyModel.model_validate(body)
                elif isinstance(my_union_type, MyModel2):
                    yield MyModel2.model_validate(body)

As you can see we can now use our new type, but its a bit messy, no? We are calling the same method model_validate but the only problem is, we can't refer to the type in a dynamic way and just say "a unknown basemodel" like this.

And, what's more, for each type we want it to handle we have to repeat this same logic...

Step in TypeVar... TypeVar allows us to specify a variable for a Type, as a opposed to defining a type as a type.

If that doesn't make sense, think of it this way:

# this is a type stored in a variable
my_model_as_a_type = MyModel

# this is an instance stored in a variable
mymodel_as_an_instance = MyModel()

As you can see, a type is literally the thing that defines what that object looks like, but it cannot be used as that object, because it is not an instance of it.

An instance is that thing, initialised in memory, with all the functions and whatever else that implementation of its type has defined.

So, moving onto typevar... how can we improve our code then?

Well, with a few simple changes we can make our function accept any basemodel, but not explicitly say it has to be BaseModel itself, e.g. can be a child or whatever and that the caller of the function can see the type returned to them...

class MyModel(BaseModel):
    ...

class MyModel2(BaseModel):
    ...

T = TypeVar("T", bound=BaseModel)

class MyClass:
    def get_records(
        model_type: Type[T],
        collection_path: str
    ) -> Generator[T, None, None]:
        adapter = TypeAdapter(model_type)
        for record in self.client.collection(collection_path).where(...).stream(...):
            body = record.to_dict()
            if body:
                yield adapter.validate_python(body)

Now when you pass in a model to this function, the object you get back will be an instance from the firestore collection of the type you gave it. And typecheckers are happy, and your colleagues are happy because now they know what the type being returned is, instead of it "possibly" being a union and the output being a "variable" of whatever model happens to be passed in and leaving it to the caller to then figure out which of the union types it is, you can have functions that are defined to only operate on specific collections for specific types...

So we could then have get_my_model_records and get_my_model_2_records function that yield the results of get_records as another generator, and merely provide their specific types to it and whatever else specific implementation details you need, meaning the consumers of your API for these two methods, know exactly what objects they will get back when they call these functions and can easily see that each method is specific for each type.