Add NewType (#226)
diff --git a/pep-0484.txt b/pep-0484.txt
index e9ac4d0..a1019c3 100644
--- a/pep-0484.txt
+++ b/pep-0484.txt
@@ -1173,6 +1173,68 @@
 in expressions, while type comments only apply to assignments.
 
 
+NewType helper function
+-----------------------
+
+There are also situations where a programmer might want to avoid logical
+errors by creating simple classes. For example::
+
+  class UserId(int):
+      pass
+
+  get_by_user_id(user_id: UserId):
+      ...
+
+However, this approach introduces a runtime overhead. To avoid this,
+``typing.py`` provides a helper function ``NewType`` that creates
+simple unique types with almost zero runtime overhead. For a static type
+checker ``Derived = NewType('Derived', Base)`` is roughly equivalent
+to a definition::
+
+class Derived(Base):
+    def __init__(self, _x: Base) -> None:
+        ...
+
+While at runtime, ``NewType('Derived', Base)`` returns a dummy function
+that simply returns its argument. Type checkers require explicit casts
+from ``int`` where ``UserId`` is expected, while implicitly casting
+from ``UserId`` where ``int`` is expected. Examples::
+
+        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 only one argument that shoud be a proper class,
+i.e., not a type construct like ``Union``, etc. The function returned
+by ``NewType`` accepts only one argument; this is equivalent to supporting
+only one constructor accepting an instance of the base class (see above).
+Example::
+
+  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
+
+Both ``isinstance`` and ``issubclass``, as well as subclassing will fail
+for ``NewType('Derived', Base)`` since function objects don't support
+these operations.
+
+
 Stub Files
 ==========
 
@@ -1562,6 +1624,9 @@
   This is useful to declare the types of the fields of a named tuple
   type.
 
+* NewType, used to create unique types with little runtime overhead
+  ``UserId = NewType('UserId', int)``
+
 * cast(), described earlier
 
 * @no_type_check, a decorator to disable type checking per class or
diff --git a/python2/test_typing.py b/python2/test_typing.py
index b801383..7cce3a5 100644
--- a/python2/test_typing.py
+++ b/python2/test_typing.py
@@ -15,6 +15,7 @@
 from typing import Generic
 from typing import cast
 from typing import Type
+from typing import NewType
 from typing import NamedTuple
 from typing import IO, TextIO, BinaryIO
 from typing import Pattern, Match
@@ -1141,6 +1142,25 @@
         joe = new_user(BasicUser)
 
 
+class NewTypeTests(BaseTestCase):
+
+    def test_basic(self):
+        UserId = NewType('UserId', int)
+        UserName = NewType('UserName', str)
+        self.assertIsInstance(UserId(5), int)
+        self.assertIsInstance(UserName('Joe'), type('Joe'))
+        self.assertEqual(UserId(5) + 1, 6)
+
+    def test_errors(self):
+        UserId = NewType('UserId', int)
+        UserName = NewType('UserName', str)
+        with self.assertRaises(TypeError):
+            issubclass(UserId, int)
+        with self.assertRaises(TypeError):
+            class D(UserName):
+                pass
+
+
 class NamedTupleTests(BaseTestCase):
 
     def test_basics(self):
diff --git a/python2/typing.py b/python2/typing.py
index adbf038..48c9d4a 100644
--- a/python2/typing.py
+++ b/python2/typing.py
@@ -61,6 +61,7 @@
     'AnyStr',
     'cast',
     'get_type_hints',
+    'NewType',
     'no_type_check',
     'no_type_check_decorator',
     'overload',
@@ -1579,6 +1580,35 @@
     return cls
 
 
+def NewType(name, tp):
+    """NewType creates simple unique types with almost zero
+    runtime overhead. NewType(name, tp) is considered a subtype of tp
+    by static type checkers. At runtime, NewType(name, tp) returns
+    a dummy function that simply returns its argument. Usage::
+
+        UserId = NewType('UserId', int)
+
+        def name_by_id(user_id):
+            # type: (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
+    """
+
+    def new_type(x):
+        return x
+
+    # Some versions of Python 2 complain because of making all strings unicode
+    new_type.__name__ = str(name)
+    new_type.__supertype__ = tp
+    return new_type
+
+
 # Python-version-specific alias (Python 2: unicode; Python 3: str)
 Text = unicode
 
diff --git a/src/test_typing.py b/src/test_typing.py
index ade8a35..432bb15 100644
--- a/src/test_typing.py
+++ b/src/test_typing.py
@@ -16,6 +16,7 @@
 from typing import get_type_hints
 from typing import no_type_check, no_type_check_decorator
 from typing import Type
+from typing import NewType
 from typing import NamedTuple
 from typing import IO, TextIO, BinaryIO
 from typing import Pattern, Match
@@ -1401,6 +1402,25 @@
         joe = new_user(BasicUser)
 
 
+class NewTypeTests(BaseTestCase):
+
+    def test_basic(self):
+        UserId = NewType('UserId', int)
+        UserName = NewType('UserName', str)
+        self.assertIsInstance(UserId(5), int)
+        self.assertIsInstance(UserName('Joe'), str)
+        self.assertEqual(UserId(5) + 1, 6)
+
+    def test_errors(self):
+        UserId = NewType('UserId', int)
+        UserName = NewType('UserName', str)
+        with self.assertRaises(TypeError):
+            issubclass(UserId, int)
+        with self.assertRaises(TypeError):
+            class D(UserName):
+                pass
+
+
 class NamedTupleTests(BaseTestCase):
 
     def test_basics(self):
diff --git a/src/typing.py b/src/typing.py
index b7f3ffa..289abf5 100644
--- a/src/typing.py
+++ b/src/typing.py
@@ -64,6 +64,7 @@
     'AnyStr',
     'cast',
     'get_type_hints',
+    'NewType',
     'no_type_check',
     'no_type_check_decorator',
     'overload',
@@ -1632,6 +1633,33 @@
     return cls
 
 
+def NewType(name, tp):
+    """NewType creates simple unique types with almost zero
+    runtime overhead. NewType(name, tp) is considered a subtype of tp
+    by static type checkers. At runtime, NewType(name, tp) returns
+    a dummy function that simply returns its argument. Usage::
+
+        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
+    """
+
+    def new_type(x):
+        return x
+
+    new_type.__name__ = name
+    new_type.__supertype__ = tp
+    return new_type
+
+
 # Python-version-specific alias (Python 2: unicode; Python 3: str)
 Text = str