NewTypes
There are situations where you may want to avoid programming errors bycreating simple derived classes that are only used to distinguishcertain values from base class instances. Example:
- class UserId(int):
- pass
- def get_by_user_id(user_id: UserId):
- ...
However, this approach introduces some runtime overhead. To avoid this, the typingmodule provides a helper function NewType
that creates simple unique types withalmost zero runtime overhead. Mypy will treat the statementDerived = NewType('Derived', Base)
as being roughly equivalent to the followingdefinition:
- class Derived(Base):
- def __init__(self, _x: Base) -> None:
- ...
However, at runtime, NewType('Derived', Base)
will return a dummy function thatsimply returns its argument:
- def Derived(_x):
- return _x
Mypy will require explicit casts from int
where UserId
is expected, whileimplicitly casting from UserId
where int
is expected. Examples:
- from typing import NewType
- UserId = NewType('UserId', int)
- def name_by_id(user_id: UserId) -> str:
- ...
- UserId('user') # Fails type check
- name_by_id(42) # Fails type check
- name_by_id(UserId(42)) # OK
- num = UserId(5) + 1 # type: int
NewType
accepts exactly two arguments. The first argument must be a string literalcontaining the name of the new type and must equal the name of the variable to which the newtype is assigned. The second argument must be a properly subclassable class, i.e.,not a type construct like Union
, etc.
The function returned by NewType
accepts only one argument; this is equivalent tosupporting only one constructor accepting an instance of the base class (see above).Example:
- from typing import NewType
- class PacketId:
- def __init__(self, major: int, minor: int) -> None:
- self._major = major
- self._minor = minor
- TcpPacketId = NewType('TcpPacketId', PacketId)
- packet = PacketId(100, 100)
- tcp_packet = TcpPacketId(packet) # OK
- tcp_packet = TcpPacketId(127, 0) # Fails in type checker and at runtime
You cannot use isinstance()
or issubclass()
on the object returned byNewType()
, because function objects don’t support these operations. You cannotcreate subclasses of these objects either.
Note
Unlike type aliases, NewType
will create an entirely new andunique type when used. The intended purpose of NewType
is to help youdetect cases where you accidentally mixed together the old base type and thenew derived type.
For example, the following will successfully typecheck when using typealiases:
- UserId = int
- def name_by_id(user_id: UserId) -> str:
- ...
- name_by_id(3) # ints and UserId are synonymous
But a similar example using NewType
will not typecheck:
- from typing import NewType
- UserId = NewType('UserId', int)
- def name_by_id(user_id: UserId) -> str:
- ...
- name_by_id(3) # int is not the same as UserId