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
Answer from jirassimok on Stack Overflow
🌐
GitHub
github.com › python › typing › issues › 189
Add NewType() to create simple unique types with zero runtime overhead · Issue #189 · python/typing
December 27, 2015 - The description is best gleaned from the following mypy issue: python/mypy#1284 (comment) and following. We're going with option (A). Really brief example: from typing import NewType UserId = NewType('UserId', int) Now to the type checke...
Author   gvanrossum
🌐
Python.org
discuss.python.org › ideas
Introduce new syntax to create `NewType` - Ideas - Discussions on Python.org
October 11, 2023 - With PEP 695 accepted in Python 3.12, the soft keyword type can be used to create type aliases. # Old: from typing import TypeAlias Point: TypeAlias = tuple[float, float] # New: type Point = tuple[float, float] However, the current way to create NewTypes involves importing from the typing module, and providing a name string, which is unintuitive: from typing import NewType OrderId = NewType("OrderId", int) Unlike type aliases, NewTypes don’t have first-class syntax support.
Discussions

Introduction to a new runtime Python typing library - `python-newtype`! - Typing - Discussions on Python.org
🚀 Hey everyone! I’m beyond excited to share python-newtype, a new Python C-extension library I’ve been working on that makes Domain-Driven Development (DDD) not only easier but also super fast! 🐍⚡ Here’s what makes python-newtype stand out: It’s built with a C-extension under ... More on discuss.python.org
🌐 discuss.python.org
5
December 13, 2024
python - What is the difference between TypeVar and NewType? - Stack Overflow
TypeVar and NewType seem related but I'm not sure when I'm supposed to use each or what the difference is at runtime and statically. More on stackoverflow.com
🌐 stackoverflow.com
`NewType` pattern in Python
In Rust and Golang, there is this popular pattern called the new type pattern where we can define types that are a subtype of the supertype and implement methods on the subtype that constrains the value of the subtype itself by holding certain invariances. For example, we can make a new type ... More on discuss.python.org
🌐 discuss.python.org
0
1
August 16, 2023
Structural Pattern Matching - Add support for `typing.NewType` and `typing.TypeAliasType` - Ideas - Discussions on Python.org
TL;DR Change typing.NewType and typing.TypeAliasType to be metaclasses or class constructors to allow pattern matching and isinstance checks. The Problem typing.NewType is here to remove type confusion between string ty… More on discuss.python.org
🌐 discuss.python.org
5
July 2, 2025
🌐
Python documentation
docs.python.org › 3 › library › typing.html
typing — Support for type hints
3 weeks ago - Doing Derived = NewType('Derived', Original) will make the static type checker treat Derived as a subclass of Original, which means a value of type Original cannot be used in places where a value of type Derived is expected.
🌐
Python.org
discuss.python.org › typing
Introduction to a new runtime Python typing library - `python-newtype`! - Typing - Discussions on Python.org
December 13, 2024 - 🚀 Hey everyone! I’m beyond excited to share python-newtype, a new Python C-extension library I’ve been working on that makes Domain-Driven Development (DDD) not only easier but also super fast! 🐍⚡ Here’s what makes python-newtype stand out: It’s built with a C-extension under the hood, which means it’s optimized for performance.
🌐
Mypy
mypy.readthedocs.io › en › stable › more_types.html
More types - mypy 1.19.1 documentation
NewType lets you define a variant of a type that is treated as a separate type by mypy but is identical to the original type at runtime.
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

🌐
Vaibhavkarve
vaibhavkarve.github.io › pytype.html
Python's Type theory
# define a new type PositiveInt = NewType('PositiveInt', int) # This is the same as declaring PositiveInt <: int # define a constructor function for the new type def make_positive_int(a: int) -> PositiveInt: if a > 0: return PositiveInt(a) raise ValueError, 'arg was not positive.'
Find elsewhere
🌐
Asyncmove
py-nt.asyncmove.com
python-newtype
python-newtype is unique in its implementation of the "new type" concept from type theory. The key feature is its ability to maintain subtype relationships through method calls and provide robust type validation.
🌐
Python.org
discuss.python.org › python help
`NewType` pattern in Python - Python Help - Discussions on Python.org
August 16, 2023 - In Rust and Golang, there is this ... we can define types that are a subtype of the supertype and implement methods on the subtype that constrains the value of the subtype itself by holding certain invariances....
🌐
Python.org
discuss.python.org › ideas
Structural Pattern Matching - Add support for `typing.NewType` and `typing.TypeAliasType` - Ideas - Discussions on Python.org
July 2, 2025 - The Problem typing.NewType is here to remove type confusion between string types, string types, string types, and string types [1], and only a few of them have strictly-typed counterparts (like pathlib.Path), as well as other similar cases. ...
🌐
Python
typing.python.org › en › latest › spec › aliases.html
Type aliases — typing documentation
NewType accepts exactly two arguments: a name for the new unique type, and a base class. The latter should be a proper class (i.e., not a type construct like Union, etc.), or another unique type created by calling NewType. The callable returned by NewType accepts only one argument; this is equivalent to supporting only one constructor accepting an instance of the base class (see above).
🌐
GitHub
github.com › python › typing › issues › 746
repr of NewType · Issue #746 · python/typing
August 14, 2020 - Currently NewType has a rather unsatisfactory repr: from typing import NewType A = NewType("A", int) print(repr(A)) outputs: .new_type at MEMORYADDRESS&...
Author   hoodmane
🌐
Python.org
discuss.python.org › ideas
Allow for a `convert=` flag in typing.NewType to automatically convert between compatible types - Ideas - Discussions on Python.org
October 23, 2022 - The context The NewType feature of Python’s static typing is extremely useful for ensuring that different types of otherwise similar data aren’t mingled (for example mixing UserIds with PostIds on a social media platform). One of my projects is effectively taking advantage of them to ensure ...
🌐
Realphysics
realphysics.info › Theory of Python › classes.html
Creating a New Type — Real Physics documentation
August 18, 2024 - In Python, when you create a new type, you need to call the type function.
🌐
Towards Data Science
towardsdatascience.com › home › latest › python type hinting: from type aliases to type variables and new types
Python Type Hinting: From Type Aliases To Type Variables and New Types | Towards Data Science
3 weeks ago - And so, here we are, we got what we wanted. Although the task did not seem overly complex, type aliases were not enough to achieve our aim. Nevertheless, typing‘s type variables (TypeVar) and new types (NewType) came to rescue. Type hints are not required in Python; they are optional.
🌐
Austin
austin.ky › blog › python-typing-newtype
Consider Python's NewType Instead of an Alias - Justin Austin
December 19, 2020 - Doing Derived = NewType('Derived', Original) will make the static type checker treat Derived as a subclass of Original, which means a value of type Original cannot be used in places where a value of type Derived is expected.
🌐
Runebook.dev
runebook.dev › en › docs › python › library › typing › typing.NewType.__supertype__
Python Typing: Common Issues with NewType and Alternatives (Custom Classes vs. Type Aliases)
First, let's understand what typing. NewType does. In Python, the typing module helps with static type checking.The NewType helper function creates a distinct type that a static type checker (like mypy) treats as a subclass of the original base type
🌐
Reddit
reddit.com › r/learnpython › should i use a type alias or typing.newtype?
r/learnpython on Reddit: Should I use a type alias or typing.NewType?
April 10, 2021 -

I want to create a type for matrices. Should I use a type alias or typing.NewType?

Matrix = NewType('Matrix', List[List[int]]) or simply Matrix = List[List[int]] ?