Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

typing.NewType is a utility used to create distinct, strongly-typed aliases for existing types that are enforced exclusively by static type checkers. At runtime, a NewType is completely transparent, introduces zero overhead, and resolves directly to its original base type.

Syntax

from typing import NewType


# NewType(name: str, tp: Type)
DistinctType = NewType('DistinctType', BaseType)

Static Type Checker Behavior

To static type analyzers (like mypy or pyright), NewType creates a unidirectional inheritance relationship. The analyzer treats the NewType as a subclass of the base type, enforcing standard subtyping rules (the Liskov Substitution Principle).
  1. Subtyping: A NewType can be passed to any function expecting the base type, just as a subclass can be used in place of its base class.
  2. Strictness: The base type cannot be passed to a function expecting the NewType.
  3. Operation resolution: Performing standard operations (like arithmetic) on a NewType yields the base type, not the NewType.
from typing import NewType

UserId = NewType('UserId', int)

def require_int(val: int) -> None: ...
def require_user_id(val: UserId) -> None: ...


# Instantiation requires explicit casting
uid = UserId(42)

require_int(uid)       # PASS: UserId is treated as a subclass of int
require_user_id(42)    # FAIL: int cannot be implicitly cast to UserId


# Operations revert to the base type
result = uid + 1       # 'result' is inferred as 'int', not 'UserId'

Runtime Behavior

At runtime, NewType does not create a new class or allocate new memory structures.
  • Pre-Python 3.10: NewType returned a standard nested function: def new_type(x): return x.
  • Python 3.10+: NewType returns an instance of the typing.NewType class, which implements a __call__ method that acts as an identity function, returning the exact object passed to it.
Because it does not generate a true Python class, it is subject to strict runtime limitations:
from typing import NewType

UserId = NewType('UserId', int)
uid = UserId(42)


# 1. Type identity remains the base type
print(type(uid))  # <class 'int'>


# 2. isinstance() and issubclass() checks will fail
isinstance(uid, UserId)  # TypeError: isinstance() arg 2 must be a type, a tuple of types, or a union


# 3. Cannot be subclassed
class AdminId(UserId):   # TypeError: NewType.__init__() takes 3 positional arguments but 4 were given
    pass

NewType vs. TypeAlias

It is critical to distinguish NewType from standard type aliases.
from typing import TypeAlias, NewType


# TypeAlias: Completely interchangeable in static analysis
AliasInt: TypeAlias = int 


# NewType: Distinct type in static analysis
StrictInt = NewType('StrictInt', int)

def process_alias(val: AliasInt) -> None: ...
def process_strict(val: StrictInt) -> None: ...

process_alias(42)   # PASS
process_strict(42)  # FAIL
Master Python with Deep Grasping Methodology!Learn More