The purpose of NewType is purely for static type checking, but for dynamic purposes it produces the wrapped type. It does not make a new type at all, it returns a callable that the static type checker can see, that's all.

When you do:

x = MyStr("Hello World")

it doesn't produce a new instance of MyStr, it returns "Hello World" entirely unchanged, it's still the original str passed in, even down to identity:

>>> s = ' '.join(["a", "b", "c"])  # Make a new string in a way that foils interning, just to rule out any weirdness from caches
>>> ms = MyStr(s)  # "Convert" it to MyStr
>>> type(ms)       # It's just a str
str
>>> s is ms        # It's even the *exact* same object you passed in
True

The point is, what NewType(...) returns is effectively a callable that:

  1. Acts as the identity function (it returns whatever you give it unchanged); in modern Python, NewType itself is a class that begins with __call__ = _idfunc, that's literally just saying when you make a call with an instance, return the argument unchanged.
  2. (In modern Python) Has some useful features like overloading | to produce Unions like other typing-friendly things.

but you can't use it usefully for isinstance, not because it's not producing instances of anything.

As other answers have mentioned, if you need runtime, dynamic checking, subclassing is the way to go. The other answers are doing both more (unnecessarily implementing __new__) and less (allowing arbitrary attributes, bloating instances of the subclass for a benefit you won't use) than necessary, so here's what you want for something that:

  1. Is considered a subclass of str for both static and runtime checking purposes
  2. Does not bloat the memory usage per-instance any more than absolutely necessary
class MyStr(str):   # Inherit all behaviors of str
    __slots__ = ()  # Prevent subclass from having __dict__ and __weakref__ slots, saving 16 bytes
                    # per instance on 64 bit CPython builds, and avoiding weirdness like allowing
                    # instance attributes on a logical str

That's it, just two lines (technically, a one-liner like class MyStr(str): __slots__ = () is syntactically legal, but it's bad style, so I avoid it), and you've got what you need.

Answer from ShadowRanger on Stack Overflow
Top answer
1 of 7
2

The purpose of NewType is purely for static type checking, but for dynamic purposes it produces the wrapped type. It does not make a new type at all, it returns a callable that the static type checker can see, that's all.

When you do:

x = MyStr("Hello World")

it doesn't produce a new instance of MyStr, it returns "Hello World" entirely unchanged, it's still the original str passed in, even down to identity:

>>> s = ' '.join(["a", "b", "c"])  # Make a new string in a way that foils interning, just to rule out any weirdness from caches
>>> ms = MyStr(s)  # "Convert" it to MyStr
>>> type(ms)       # It's just a str
str
>>> s is ms        # It's even the *exact* same object you passed in
True

The point is, what NewType(...) returns is effectively a callable that:

  1. Acts as the identity function (it returns whatever you give it unchanged); in modern Python, NewType itself is a class that begins with __call__ = _idfunc, that's literally just saying when you make a call with an instance, return the argument unchanged.
  2. (In modern Python) Has some useful features like overloading | to produce Unions like other typing-friendly things.

but you can't use it usefully for isinstance, not because it's not producing instances of anything.

As other answers have mentioned, if you need runtime, dynamic checking, subclassing is the way to go. The other answers are doing both more (unnecessarily implementing __new__) and less (allowing arbitrary attributes, bloating instances of the subclass for a benefit you won't use) than necessary, so here's what you want for something that:

  1. Is considered a subclass of str for both static and runtime checking purposes
  2. Does not bloat the memory usage per-instance any more than absolutely necessary
class MyStr(str):   # Inherit all behaviors of str
    __slots__ = ()  # Prevent subclass from having __dict__ and __weakref__ slots, saving 16 bytes
                    # per instance on 64 bit CPython builds, and avoiding weirdness like allowing
                    # instance attributes on a logical str

That's it, just two lines (technically, a one-liner like class MyStr(str): __slots__ = () is syntactically legal, but it's bad style, so I avoid it), and you've got what you need.

2 of 7
1

Cross-reference: inheritance from str or int

Even more detailed in the same question: https://stackoverflow.com/a/2673802/1091677


If you would like to subclass Python's str, you would need to do the following way:

class MyStr(str):
  # Class instances construction in Python follows this two-step call:
  # First __new__, which allocates the immutable structure,
  # Then __init__, to set up the mutable part.
  # Since str in python is immutable, it has no __init__ method.
  # All data for str must be set at __new__ execution, so instead
  # of overriding __init__, we override __new__:
  def __new__(cls, *args, **kwargs):
    return str.__new__(cls, *args, **kwargs)

Then:

x = MyStr("Hello World")

isinstance(x, MyStr)

returns True as expected

🌐
GitHub
github.com › python › mypy › issues › 3325
Runtime check against underlying type of NewType · Issue #3325 · python/mypy
March 6, 2017 - # still in flux; may become 'float' or other type soon Timestamp = NewType('Timestamp', int) def _validate_timestamp(n: Any) -> Timestamp: if isinstance(n, ???): # ???
Author   rkr-at-dbx
🌐
Python.org
discuss.python.org › python help
How to `isinstance` a typing type so that it works with static type checkers? - Python Help - Discussions on Python.org
January 25, 2024 - My code defines lots of typing types, and I need to isinstance lots of objects against them at runtime. Something like this: Type1 = list[int] Type2 = list[str] # etc. # get data from the outside, e.g. read from a file data = get_data() # NOT WORKING if isinstance(data, Type1): "do something" elif isinstance(data, Type2): "do something else" else: "etc."
🌐
Anselmos Blog
witkowskibartosz.com › blog › python_typing_support_for_type_hints_type_aliases_and_newtype.html
Python typing support for type hints - type aliases and NewType - Anselmos Blog
March 28, 2018 - class NewIntInheritance(int): pass ... NewIntTyping(2))) They will basically do the same. There is a difference. When you create a new type with inheritance, that you can use isinstance with it....
🌐
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.
🌐
Python documentation
docs.python.org › 3 › library › typing.html
typing — Support for type hints
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
A canonical "isinstance" implementation for typing types? - Typing - Discussions on Python.org
March 25, 2020 - You can’t use typing types like Dict[str, int] in an isinstance check: Python 3.7.6 (default, Dec 30 2019, 19:38:28) Type 'copyright', 'credits' or 'license' for more information IPython 7.12.0 -- An enhanced Interactive Python. Type '?' for help. In [1]: from typing import Dict In [2]: myvar = {"a": 1} In [3]: isinstance(myvar, Dict[str, int]) --------------------------------------------------------------------------- TypeError Traceback (most recent call las...
Find elsewhere
🌐
Python.org
discuss.python.org › typing
Type aliases don't work with `isinstance` - Typing - Discussions on Python.org
October 13, 2025 - I use the new typestatements to make type aliases. For example, I have in my code a PathLiketype that represents the possible arguments to pathlib.Path: import os type PathLike = str | os.PathLike Now, I wanted to check whether a given variable is PathLike, so I used isinstance(p, PathLike), but it didn’t work since isinstancerequires a type, a tuple of types, or a union of types: print(isinstance(‘foo.txt’, PathLike)) TypeError: isinstance() arg 2 must be a type, a tuple of types, or a uni...
🌐
GitHub
github.com › microsoft › pyright › issues › 6828
Type created with `NewType` shouldn't be valid for `isinstance` call · Issue #6828 · microsoft/pyright
October 30, 2023 - A type created with NewType cannot be used in an isinstance call. Mypy flags this as an error, but pyright does not. from typing import NewType MyInt = NewType("MyInt", int) v = 1 isinstance(v, MyInt)
Author   erictraut
🌐
Agest
agest.am › phantom-types-in-python
Implementing Phantom types in Python | agest.am
Python exposes many interfaces for meta programming, allowing us to define object behavior on a very granular level. One of these interfaces, that is interesting for our use case, is the __instancecheck__ method. By implementing for a class we can define when an isinstance check with that class returns True. Because existing static type checkers are all compatible with isinstance, this allows us to define types similar to the NewType objects we've explored, but with stricter semantics for instantiation.
🌐
PyPI
pypi.org › project › newertype
Client Challenge
JavaScript is disabled in your browser · Please enable JavaScript to proceed · A required part of this site couldn’t load. This may be due to a browser extension, network issues, or browser settings. Please check your connection, disable any ad blockers, or try using a different browser
🌐
Python.org
discuss.python.org › python help
`NewType` pattern in Python - Python Help - Discussions on Python.org
May 30, 2023 - 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 FourCharsStr like so: from typing import TYPE_CHECKING if TYPE_CHECKING: from typing import NoReturn, Union class FourCharsStr(str): @classmethod def _validate_str(cls, value: "str") -> ...
🌐
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 - 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…
🌐
GitHub
github.com › python › mypy › issues › 6386
Inheritance broken when testing isinstance? · Issue #6386 · python/mypy
October 7, 2018 - I was able to minimize my problem to this using Python 3.5 and mypy 0.660: from abc import ABC, abstractmethod from typing import Any, Optional, Type, Union class A(ABC): pass class AA(A): pass class AB(A): pass class M(object): pass class MA(M, AA): pass class MB(M, AB): pass MYTYPES = Union[MA, MB] class T(object): def __init__(self, newtype:Type[MYTYPES]=MA) -> None: assert isinstance(newtype, type(M)) self._new_type = newtype # type: Type[MYTYPES] def test(self) -> A: ret = self._new_type() print(type(ret)) return ret x = T() print(isinstance(x.test(), A)) # True y = T(MB) print(isinstance(y.test(), A)) # True ·
Author   rldleblanc
🌐
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: This (1) doesn't refer to the name of the NewType, (2) doe...
Author   hoodmane
🌐
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)
A common mistake is treating the type returned by NewType like a regular Python class. The Trouble You cannot use standard Python tools like isinstance() or issubclass() with a NewType object, and you cannot subclass a NewType.
🌐
GitHub
github.com › agronholm › typeguard › issues › 341
`TypeError: isinstance() argument 2 cannot be a parameterized generic` when using with `NewType` that maps to a generic type · Issue #341 · agronholm/typeguard
October 1, 2022 - import typing Settings = typing.NewType("Settings", dict[str, str]) def settings() -> Settings: return Settings({"foo": "bar"}) def test_settings() -> None: assert settings()["foo"] == "bar"
Author   danielknell